PKU POJ 1014 Dividing

PKU POJ 1014 Dividing

PKU POJ 1014 Dividing

发表时间:2007年9月2日 18时7分17秒        评论/阅读( 2/ 11)
这道题的分类竟然在“简单”里面…… 
确实没有找到比较简单的方法 

动态规划: 

 1 #include < stdio.h >  
 2 bool  opt[ 60000 ]; 
 3 int  num = 0
 4 int  mid,max; 
 5 int  stone[ 7 ]; 
 6 int  input() 
 7
 8    scanf("%d%d%d%d%d%d",&stone[1],&stone[2],&stone[3],&stone[4],&stone[5],&stone[6]); 
 9    if(!stone[6]&&!stone[1]&&!stone[2]&&!stone[3]&&!stone[4]&&!stone[5]) {return 1;} //读到末行 
10    num++
11    printf("Collection #%d:\n",num); 
12    mid=0
13    for(int i=1;i<=6;i++) mid+=stone[i]*i; 
14    if (mid % 2 ==1)                                          //明显的剪枝 
15    
16        printf("Can't be divided.\n\n"); 
17        return 2
18    }
 
19    else mid/=2;                                              //我们的任务就是求opt 
20    return 0
21}
 
22
23 void  dp() 
24
25    memset(opt,0,60000);          //初始化,opt所有成员为false 
26    opt[0]=true;                        //opt[0]显然是true 
27    max=0;                             //当前最大值是0 
28
29    for (int i=1;i<=6;i++)           
30    
31        if (stone[i]>0
32        
33            for(int j=max;j>=0;j--)   // 从当前最大值开始往下算 
34            if (opt[j])                      //找到可行的j 
35            
36                if (opt[mid])              //判断是否已经求出结果 
37                
38                    printf("Can be divided.\n\n"); 
39                    return
40                }
 
41                for (int k=1;k<=stone[i];k++)         //在刚找到的可行j基础上加石头. 
42                
43                    if (j+k*i>mid || opt[j+k*i]) break;  //如果已经大于总价值的一半mid,或opt[j+k*i]已计算,跳出循环 
44                    opt[j+k*i]=true
45                }
 
46            }
 
47        }
 
48        max=max+i*stone[i];                           //更新当前最大值 
49        if (max>mid) max=mid;                        //如果当前最大值超过了mid,对其赋值为mid 
50    }
 
51    printf("Can't be divided.\n\n");                     //如果从1到6循环完一遍后仍然没有算出opt[mid],说明无解 
52}
 
53
54 int  main() 
55
56    while (1
57    
58        int t=input(); 
59        switch (t) 
60        
61            case 1 : return 0;   //读到末行,结束 
62            case 2 : continue;  //读到明显的剪枝 
63            default : dp(); 
64        }
 
65    }
 
66    return 0
67}
 



本题是找按价值均分大理石的方案是否存在,由于分配时不能破坏大理石,所以有个显而易见的剪枝:当所有的大理石的总价值为奇数时肯定不能被均分。 
把问题转化一下即:由一个人能否从原大理石堆中取出总价值为原来一半的大理石 
本题的主要算法是动态规划 
数组opt代表状态: 
设总价值为V, 
当opt[k]==true时,说明,可以有一人获得价值k,另外一人获得价值V-k的大理石分配方案。反之若opt[k]=false 说明这种分配方案不存在 
我们的任务就是计算出opt[v/2]是true还是false,即opt[mid]。 
显然有opt[0]==true的方案存在,即一个人什么都不分,另外一个人拿走全部的大理石 

设i(1<<6)为石头的价值,试想若opt[k]==true,而且在这堆总价值为k的石头中有一块石头的价值为i,那么opt[k-i]这种分配方案就是必然存在的了。反过来想,当opt[k]==true ,如果能再向k中增加一价值为i的大理石,则opt[k]==true必然成立 
石头有两个属性,一个是价值另一个是数量,这里stone[i]代表价值为i的大理石的数量,我们根据其中一个属性:价值 来划分阶段。 
即for (int i=1;i<=6;i++),opt[k]表示状态是否存在(这里的状态是指能否从原石头堆中分出价值为k的新石头堆) 
在初始阶段是i=1,初始状态是opt[0],max代表目前满足opt[k]==true这一条件的k的最大值 

for(int j=max;j>=0;j--) 
//从当前最大值opt[开始],根据前面提到的opt[j]==true成立则opt[j+i]==true亦成立的理论,在原有状态opt[j]==true已存在的条件下加入stone[i]阶段的石头,得到新的状态  


PS  :感谢重庆大学微软技术俱乐部的肖阳同学
       原来这道题果然有更简单的方法
http://www.blogjava.net/zellux/archive/2007/07/30/133416.html

你可能感兴趣的:(PKU POJ 1014 Dividing)