昨晚队内练习赛做了Greater New York Regional 2009套题...水一套..不过还有有道很经典的问题...鹰蛋问题...IOI2004年的一论文就对这个问题有过深入探讨...比赛的时候我只想到了个大概...
我的思路和论文中的方法二差不多...状态的表示dp[k][p]代表在确定层数为k时用p个球所需确定鹰蛋承受能力的最小次数...也就是输入输出的东西了...我觉得这种状态时最为直观的...在更新时可以用二分..那么时间复杂度为O(n^m*logn)..有0<n,m<=1000...可以接受....
直观的例子...首先初值dp[k][1]=k...(0<k<=1000) 显而易见...如果要更新出dp[10][2]...
最优方案: 将第一个蛋在4层扔一次..若蛋碎..问题转化为dp[3][1].. 若没碎... 再将第一个蛋在7层扔一次..若此时但碎..问题转化为dp[[2][1]...若第一个蛋还没碎..再将第一个蛋在9层扔一次..若蛋碎..问题转化为dp[1][1]..若没碎..问题转化为dp[1][1]...可见由于第一个蛋的碎与没碎..将情况分类成好些...而dp[3][1]+1是最多的..为4..所以dp[10][2]=4..
那为何在第4层失败后...第二次是扔第7层..因为知道dp[3][1]=3..那么要维持最多时dp[3][1]+1=4次..那么就要保证第一个蛋扔两次后第二个蛋只能扔2次...若两次的距离为2..dp[2][1]=2...2+2=4..那么可行...4+1+2=7..同理...7+1+1=9位第二次扔1号蛋没碎后要选择的层数..因为能保证dp[1][1]=1,1+3=4..
如何确定第一次是第4层?..由于第一次扔在哪就能直接确定后面扔在哪..且第一次扔的高度与这种方案确定的次数是同单调的...如dp[100][2]一定不会大于dp[200][2]...靠2分枚举...因为如果第一次扔第k层ok...那么第一次扔k-1也一定是ok的..
再譬如要更新dp[100][3]...二分l=-1,r=100,第一次的mid=50...
尝试...将第一个蛋先扔在50..那么就要保证往后的次数为dp[49][2]+1...所以第一个蛋若再50层没碎..第二次应该到96层...为何呢..因为第一个蛋较前种情况会多扔一次..那后两个蛋只能少扔一次...要保证两次的间隙层数=dp[49][2]-1.而dp[45][2]=dp[49][2]-1..且45是满足条件最大的(dp[46][2]=dp[47][2]=dp[48][2]=dp[49][2])...如此下去..到第一个蛋扔3次时..所能覆盖的范围超出100了...得出3个蛋第一次扔在50层一定能测试出100层之内的鹰蛋极限承受层数...那么在l=-1,r=50只能继续找答案...
这样写还是会超时..小优化..若发现蛋不断增加时..次数并没有减少..就没必要还来找了..但不能出现前后相同就跳出来直接赋值..我就这里WA了好几次...我AC的程序是判断当球增加时连续5个的值相同..则跳出直接赋值...
Program:
#include<iostream> #include<stdio.h> #include<algorithm> #include<string.h> #include<math.h> #include<map> #include<queue> #include<stack> #define ll long long #define oo 1000000000 #define pi acos(-1) using namespace std; int T,t,n,m,dp[1005][1005],f[1005]; int main() { int i,j,l,r,mid,k,x,h; memset(dp,0,sizeof(dp)); for (i=0;i<=1000;i++) dp[i][1]=i; for (i=1;i<=1000;i++) { memset(f,0,sizeof(f)); for (j=2;j<=1000;j++) { l=0; r=i; while (r-l>1) { mid=(r+l)/2; k=0; x=mid; while (k<i && x>0) { h=dp[x-1][j-1]; k+=x; while (x>0 && dp[x-1][j-1]==h) x--; } if (k>=i) r=mid; else l=mid; } k=dp[r-1][j-1]+1; f[k]++; if (f[k]>5) break; dp[i][j]=k; } for (;j<=1000;j++) dp[i][j]=k; } /* scanf("%d",&T); for (t=1;t<=T;t++) { scanf("%d%d%d",&m,&m,&n); printf("%d %d\n",t,dp[n][m]); } POJ OUTPUT*/ /* while (~scanf("%d%d",&m,&n)) { if (!n && !m ) break; printf("%d\n",dp[n][m]); } URAL OUTPUT*/ return 0; }