Ural 1223 & POJ 3783 鹰蛋问题

来源:互联网 发布:pc蛋蛋牛人10×0算法 编辑:程序博客网 时间:2024/05/10 16:15

     昨晚队内练习赛做了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;}

原创粉丝点击