[2016-2017 ACM-ICPC CHINA-Final][GYM 101194 E] Bet
题意: 有n支队伍,若给第i支队伍下注\(x_i\),第i支队伍赢的话就能获得\(x_i+\frac{B_i}{A_i}\)的收益。现在要你选出若干支队伍并下注,使得在选出的队伍中有且仅有一支队伍赢的情况下,无论是哪支队伍赢,都能赚到钱(收益大于总下注额)。求最多能选出多少支队伍。(\(n<=100,0<A_i,B_i<100\),保证\(A_i,B_i\)小数点后只有三位)
题解:假设选出了\(k\)支队伍,第i支队伍下注\(x_i\),总共下注了\(sum\),那么为了满足条件,必有\[x_i+x_i\cdot \frac{B_i}{A_i}>sum\]
将式子变形为\(x_i>\frac{A_i}{A_i+B_i}sum\),将\(i=1...n\)下的式子相加,可以得到\[sum>\sum{\frac{A_i}{A_i+B_i}}sum\]
显然选出队伍满足条件的必要条件是\(\sum{\frac{A_i}{A_i+B_i}}<1\)。下面证明其充分性。
设\(sum=1\),则\(\sum{x_i}=1\),取\(x_i=\frac{A_i}{A_i+B_i}\frac{1}{\sum{\frac{A_i}{A_i+B_i}}}>\frac{A_i}{A_i+B_i}sum\)且\(\sum{x_i}=1\),所以条件为充分条件。
显然只要按\(\frac{A_i}{A_i+B_i}\)排序,取出最多的就可以了。
式子推导很简单,但是为什么过的人这么少呢?
因为这题卡精度!
做除法时要精确到小数点后50位(据说,我直接开了100位)以及读入的时候由于会出现读入1.0存储为0.999的情况,所以读入也要手动处理过。
写了一个高精小数除,充分认识到了java的重要性以及自己的弱小。
#include <bits/stdc++.h> using namespace std; const int maxn=110; struct BigNum { int a[250],len; void Init(double x=0,int tlen=99) { memset(a,0,sizeof(a)); int t=(int)x; len=100; if (t==0) len=101; while (t) a[len++]=t%10,t/=10; x-=(int)x; if (tlen==3) { x*=1000; if (x-(int)x>0.8) t=(int)x+1; else t=(int)x; a[99]=t/100; a[98]=t/10%10; a[97]=t%10; } else for (int i=99;i>=99-tlen;i--) { x*=10; a[i]=(int)x; x-=(int)x; } while (len>1 && a[len-1]==0) len--; } BigNum() {Init();} void Print() { for (int i=max(len-1,100);i>=100;i--) printf("%d",a[i]); printf("."); for (int i=99;i>=95;i--) printf("%d",a[i]); printf("\n"); } friend BigNum operator + (const BigNum &a,const BigNum &b); friend BigNum operator - (const BigNum &a,const BigNum &b); friend BigNum operator / (const BigNum &a,const BigNum &b); friend void Mul10(BigNum *a); friend void Div10(BigNum *a); friend bool operator < (const BigNum &a,const BigNum &b); friend bool operator <= (const BigNum &a,const BigNum &b); }k[maxn],c0,c1; bool operator < (const BigNum &a,const BigNum &b) { if (a.len<b.len) return true; if (a.len>b.len) return false; for (int i=a.len-1;i>=0;i--) if (a.a[i]<b.a[i]) return true; else if (a.a[i]>b.a[i]) return false; return false; } bool operator <= (const BigNum &a,const BigNum &b) { if (a.len<b.len) return true; if (a.len>b.len) return false; for (int i=a.len-1;i>=0;i--) if (a.a[i]<b.a[i]) return true; else if (a.a[i]>b.a[i]) return false; return true; } BigNum operator + (const BigNum &a,const BigNum &b) { BigNum tmp; tmp.Init(); int md=0; tmp.len=max(a.len,b.len); for (int i=0;i<tmp.len;i++) { tmp.a[i]=a.a[i]+b.a[i]+md; md=tmp.a[i]/10; tmp.a[i]%=10; } if (md) tmp.a[tmp.len++]=md; while (tmp.len>1 && tmp.a[tmp.len-1]==0) tmp.len--; return tmp; } BigNum operator - (const BigNum &a,const BigNum &b) { BigNum tmp=a; int md=0; for (int i=0;i<tmp.len;i++) { tmp.a[i]-=b.a[i]+md; if (tmp.a[i]<0) md=1,tmp.a[i]+=10; else md=0; } while (tmp.len>1 && tmp.a[tmp.len-1]==0) tmp.len--; return tmp; } BigNum operator * (const BigNum &a,const int &b) { BigNum tmp; tmp.Init(); int md=0; for (int i=0;i<tmp.len;i++) { tmp.a[i]=tmp.a[i]*b+md; md=tmp.a[i]/10; tmp.a[i]%=10; } if (md) tmp.a[tmp.len++]=md; while (tmp.len>1 && tmp.a[tmp.len-1]==0) tmp.len--; return tmp; } void Mul10(BigNum *a) { for (int i=a->len;i>=1;i--) a->a[i]=a->a[i-1]; a->a[0]=0; a->len++; } void Div10(BigNum *a) { for (int i=0;i<a->len-1;i++) a->a[i]=a->a[i+1]; a->a[a->len-1]=0; a->len--; } BigNum operator / (const BigNum &a,const BigNum &b) { BigNum tmp,a2=a,b2=b; tmp.Init(); tmp.len=100; while (b2<=a2) Mul10(&b2),tmp.len++; for (int i=tmp.len;i>=0;i--) { while (b2<=a2 && !(b2<=c0)) tmp.a[i]++,a2=a2-b2; Div10(&b2); } while (tmp.len>1 && tmp.a[tmp.len-1]==0) tmp.len--; return tmp; } double a[maxn],b[maxn]; int n; int main() { int T; c0.Init(0); c1.Init(1); scanf("%d",&T); for (int t=1;t<=T;t++) { printf("Case #%d: ",t); scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%lf:%lf",&a[i],&b[i]); BigNum x,y; x.Init(a[i],3); y.Init(a[i]+b[i],3); x=x/y; k[i]=x; } sort(k+1,k+1+n); BigNum sum; sum.Init(0); for (int i=1;i<=n+1;i++) { if (i==n+1) { printf("%d\n",n); break; } sum=sum+k[i]; if (c1<=sum) { printf("%d\n",i-1); break; } } } return 0; }
后来发现这题竟然只卡读入精度......
#include <bits/stdc++.h> using namespace std; const int maxn=110; double a[maxn],b[maxn]; long double k[maxn]; int n; int main() { int T; scanf("%d",&T); for (int t=1;t<=T;t++) { printf("Case #%d: ",t); scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%lf:%lf",&a[i],&b[i]); k[i]=1.0*floor(1000*a[i])/(floor(1000*a[i])+floor(1000*b[i])); } sort(k+1,k+1+n); long double sum=0; for (int i=1;i<=n+1;i++) { if (i==n+1) { printf("%d\n",n); break; } sum+=k[i]; if (sum>=1.0) { printf("%d\n",i-1); break; } } } return 0; }