原题目:http://poj.org/problem?id=1014
题目大意:
有分别价值为1,2,3,4,5,6的6种物品,输入6个数字,表示相应价值的物品的数量,问一下能不能将物品分成两份,是两份的总价值相等,其中一个物品不能切开,只能分给其中的某一方,当输入六个0是(即没有物品了),这程序结束,总物品的总个数不超过20000
输出:每个测试用例占三行:
第一行: Collection #k: k为第几组测试用例
第二行:是否能分(具体形式见用例)
第三行:空白(必须注意,否则PE)
一:采用第一种深搜方式,如果sum
二: 这种搜索是合理的。每次探测都可以有六种选择,也可以想象成从六个盒子里面拿东西,每次从六个盒子里选一种,第一种出现的两种错误都可以避免掉。而且每个节点的度都是6,而且特征一样。这个非常好,对每个节点的处理都一样。很明显可以递归。符合DFS的特点。
解题思路:
有两种解决方法:
第一种是几乎百度上所有同学都热衷的多重背包,确实这题就是《背包九讲》里面的“多重背包”的应用题,直接套O(V*Σlog n[i])的模板就毫无悬念地AC了,《背包九讲》里面提供的是“多重背包+二进制优化”算法,百度上也有不少同学加入了自己的想法去进一步优化,例如利用“抽屉原理”证明并“取模优化”的可行性等,这些同学都做了不少功课,值得我们学习。
第二种方法是几乎没有同学使用的DFS,本题用DFS也能0ms跑完,可能大家都被《背包九讲》冲昏了头脑,都想着套模板去了,但又看不懂模板。呻吟“研究了背包多长时间都不完全明白”的同学不妨试试DFS。其实本来不少DP题都可以用搜索过的,大家不要钻牛角尖。
解法一:DFS
#include
using namespace std;
int amount[7] = {0};
int half_value = 0;
int flag = 0;
void DFS(int value, int pre){
if(value == half_value){
flag = 1;
return;
}
if(flag == 1){ // 到某一深度的时候接受到上层的(flag=1),那这层也要继续往上
return;
}
int i = 0;
for(i = pre; i > 0; i--){
if(amount[i]){
if(i + value <= half_value){
amount[i]--;
DFS(i + value, i);
//如果搜索到某一深度满足条件
if(flag == 1){ //回到上层
return;
}
}
}
}
}
int main(){
int testcase = 1;
while(true){
flag = 0;
int totalvalue = 0;
int N = 6;
int i = 1;
while(i <= N){
cin >> amount[i];
totalvalue += amount[i] * i;
i++;
}
if(!amount[1] && !amount[2] && !amount[3] && !amount[4] && !amount[5] && !amount[6]){
break;
}
printf("Collection #%d:\n", testcase++);
if(totalvalue % 2 != 0){
cout << "Can't be divided." << endl << endl;
continue;
}
half_value = totalvalue / 2;
DFS(0, 6); //注意由于6的价值比较大容易接近所要得的一半
//若取1可能会多走很多
if(flag){
cout << "Can be divided." << endl;
} else {
cout << "Can't be divided." << endl;
}
cout << endl;
}
return 0;
}
(上面这个代码有个bug(题目还是可以AC的)( 0 0 3 0 3 1) 就过不了,上面是不能跳过中间值的,
而数据是要6和3结合于是就呵呵了(求大牛赐教怎么修改)
当然还有解法二:
//Memory Time
//656K 16MS
/*多重背包+二进制优化*/
#include
using namespace std;
int n[7]; //价值为i的物品的个数
int v; //背包容量
int SumValue; //物品总价值
bool flag; //标记是否能平分SumValue
int dp[100000]; //状态数组
int max(int a,int b)
{
return a>b?a:b;
}
/*完全背包*/
void CompletePack(int cost,int weight)
{
for(int i=cost;i<=v;i++)
{
dp[i]=max(dp[i],dp[i-cost]+weight);
if(dp[i]==v) //剪枝,当能够平分SumValue时退出
{
flag=true;
return;
}
}
return;
}
/*01背包*/
void ZeroOnePack(int cost,int weight)
{
for(int i=v;i>=cost;i--)
{
dp[i]=max(dp[i],dp[i-cost]+weight);
if(dp[i]==v) //剪枝
{
flag=true;
return;
}
}
return;
}
/*多重背包*/
void MultiplePack(int cost,int weight,int amount)
{
if(cost*amount>=v)
{
CompletePack(cost,weight);
return;
}
if(flag) //剪枝
return;
/*二进制优化*/
int k=1;
while(k>n[1]>>n[2]>>n[3]>>n[4]>>n[5]>>n[6])
{
SumValue=0; //物品总价值
for(i=1;i<=6;i++)
SumValue+=i*n[i];
if(SumValue==0)
break;
if(SumValue%2) //sum为奇数,无法平分
{
cout<<"Collection #"<
版权声明:本文为博主原创文章,未经博主允许不得转载。