题目如下:
对于每一组样例,给你六种玻璃球,每种有着不同的价值,现在问你,你能否在不拆分同一颗玻璃球价值的同时分成价值相同的两份?
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int n1=0,a[120001],cost[101],value[101],sum=0,num[7];
int main()
{
int time=0;
while(1)
{
sum=0;
n1=0;
for(int i=1;i<=6;i++)
{
scanf("%d",num+i);
sum+=num[i]*i;
}
if(sum==0) break;
if(sum%2==1){
printf("Collection #%d:\n",++time);
printf("Can't be divided.\n\n");
continue;
}
memset(a,0,sizeof(a));
for(int i=1;i<=6;i++)
{
int s=num[i],t=1;
while(s>=t)
{
if(t*i)
{
cost[++n1]=t*i;
value[n1]=t*i;
}
s-=t;
t*=2;
}
if(s*i){
cost[++n1]=s*i;
value[n1]=s*i;
}
}
for(int i=1;i<=n1;i++)
{
for(int j=sum/2;j>=cost[i];j--)
{
a[j]=max(a[j],a[j-cost[i]]+value[i]);
}
}
if(sum/2==a[sum/2]) {printf("Collection #%d:\n",++time);printf("Can be divided.\n\n");}
else {printf("Collection #%d:\n",++time);
printf("Can't be divided.\n\n");}
}
}
1.对于多重背包,首先要进行二进制优化。如果是0颗该种玻璃球,则直接忽略过去。(代码中的if(ti),if(si));
2.对于广泛的背包问题,如维护了一个a[1000]的数组,那么下标为x,就严格地代表x这个容量下最多可以容纳最多a【x】价值的东西。
之前的错误理解在于,譬如说01背包的逆序,是盲目的进行“拿不拿”的判断,而之前的状态不论存在与否。这是错误的。当维护出来a【x】时,哪怕a【x-cost【i】】不涉及该状态,那么其也应该不小于第x变量前面“设计状态”的价值。
举个例子。
我现在有重量和价值分别为1,5,6的玻璃球。
我要维护一个背包数组。
先考虑价值为6的玻璃球,维护后:
0 0 0 0 0 6 6 6 6 6 6 6
再考虑价值为5的玻璃球
0 0 0 0 5 6 6 6 6 6 11 11
再考虑价值为1的玻璃球
0 1 1 1 5 6 7 7 7 7 11 12
你品 你细品。
3.定义:
若使得价值==重量,进而
若维护出来一个背包数组a【13】,若对于下标为x的变量a【x】,如果在x这个容量下,塞下物品的重量恰好是x,则称为“真装填”,否则称之为“假装填”。
举个例子。我要装填重量分别为2,4,5的三件物品。则容量为11的背包能够容纳,则a【11】为真装填。但是如果给你一个a【12】的背包,多出的一个容量啥用没有,只能顺承容量为11时背包的数据,则这就是假装填。
这个原理可以应用于判断物品装载可不可能恰好到达一个容量v(真装填)。
这一道题就是这样。如果判断出sum/2= =a[sum/2],则认定为在sum/2容量下能够真装填,则可以分开。
也可以这样理解:等于重量的value是“背包实际装载量”,而下标x的容量为“最大装载量”,当且仅当背包实际装载量等于最大装载量时,是真装载。