[USACO18OPEN]Talent Show 解题报告
题目:传送门(洛谷)
这道题可谓刷新了我对0/1分数划分的认识。
之前做过一道最小生成树+0/1分数划分(在我的博客里也有),但这次写的时候没联想到背包问题,考场(noip模拟)上直接上了遗传然后老师多次测评取最低值就只12分了qwq。
如果对0/1分数划分不熟悉的读者可以戳这里阅读相关文章(我觉得我写得还不错(雾))
首先不难发现我们在标操之后,要选出一些牛,这些牛的w(体重)和要大于等于W(体重下限),且他们的评估函数(t-mid*w)之和是否大于等于0决定了mid是大了还是小了(差点写成中考经典病句)。
而怎么选这些牛,就是一个相对经典的背包问题,dp[v]表示用了v的体重,能收集到的最大评估函数的和。
套进背包问题的模板里就行了。
那么代码如下:
#include<iostream> #include<cstdio> #define maxn 300 using namespace std; inline void read(int &x) { x=0;int f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } inline void read(double &x) { x=0;int f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } int N,W; double w[maxn],t[maxn]; double c[maxn],dp[1005]; bool check(double mid) { for(int i=1;i<=N;i++) c[i]=t[i]-mid*w[i]; for(int i=0;i<=W;i++) dp[i]=-0x3f3f3f3f; dp[0]=0; /* for(int v=0;v<=W;v++) { for(int i=1;i<=N;i++) { int tmp=v+w[i]; if(tmp>W) tmp=W; dp[tmp]=max(dp[tmp],dp[v]+c[i]); } }*/ for(int i=1;i<=N;i++) for(int v=W;v>=0;v--) { int tmp=v+w[i]; if(tmp>W) tmp=W; dp[tmp]=max(dp[tmp],dp[v]+c[i]); } // cout<<mid<<" "<<dp[W]<<endl; if(dp[W]>=0.0) return 1; else return 0; } int main() { read(N);read(W); double tw=0,tt=0; for(int i=1;i<=N;i++) { read(w[i]); read(t[i]); tw+=w[i]; tt+=t[i]; } double l=0,r=100005,mid,ans; while(r-l>0.00001) { mid=(r+l)/2; //cout<<l<<" "<<r<<" "; if(check(mid)) l=mid,ans=mid; else r=mid; } ans*=1000; printf("%d",int(ans)); }
--------------------Warning:下面的内容不是正解!!!------------------------
然后我在考场上写的伪遗传在遗传8000次左右可以保证正确,但是时间会高达10s左右。
我提交的版本为了保证时间,只遗传了400代(事实证明一秒内我可以遗传800代甚至更多)。
为了保证时间,我甚至没有写交换。
代码如下:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<algorithm> #define maxn 300 #define yep cout<<"yep"<<endl; using namespace std; inline void read(double &x) { x=0;int f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } double N,W; double w[maxn],t[maxn]; struct node{ int u[maxn]; double f; void cal() { double wt=0,tt=0; for(int i=1;i<=N;i++) if(u[i]) wt+=w[i],tt+=t[i]; if(wt<W) f=-1; else f=tt/wt; } friend bool operator < (node a,node b) { return a.f>b.f; } }group[1005]; inline void getnew(int x) { for(int i=1;i<=N;i++) group[x].u[i]=rand()%2; group[x].cal(); } node fuck(node fa,node ma) { int c; node child; for(int i=1;i<=N;i++) { c=rand()%2; if(c) child.u[i]=fa.u[i]; else child.u[i]=ma.u[i]; } child.cal(); return child; } int main() { freopen("talent.in","r",stdin); freopen("talent.out","w",stdout); srand(time(NULL)); read(N);read(W); double tw=0,tt=0; for(int i=1;i<=N;i++){read(w[i]);read(t[i]);} int T=400; for(int i=1;i<=T;i++) { while(group[i].f<=0) getnew(i); } for(int i=1;i<=T;i++) { sort(group+1,group+T+1); for(int i=T/2+1;i<=T;i++) group[i]=fuck(group[i-T/2],group[i-T/2+1]); } sort(group+1,group+T+1); double ans; ans=group[1].f; ans*=1000; printf("%d",int(ans)); }