Ural 1223 & POJ 3783 鹰蛋问题

     昨晚队内练习赛做了Greater New York Regional 2009套题...水一套..不过还有有道很经典的问题...鹰蛋问题...IOI2004年的一论文就对这个问题有过深入探讨...比赛的时候我只想到了个大概...

     我的思路和论文中的方法二差不多...状态的表示dp[k][p]代表在确定层数为k时用p个球所需确定鹰蛋承受能力的最小次数...也就是输入输出的东西了...我觉得这种状态时最为直观的...在更新时可以用二分..那么时间复杂度为O(n^m*logn)..有0

     直观的例子...首先初值dp[k][1]=k...(0

          最优方案: 将第一个蛋在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
#include
#include
#include
#include
#include
#include
#include
#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 (k0)
                         {
                               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;
}

你可能感兴趣的:(动态规划)