1014 题目大意:
两个娃子分弹珠,有六种可能(1-6)的好坏程度的弹珠,给定每种好坏弹珠的数量,求是否存在使好坏度总和均分的分配方案。
1026 题目大意:
一台出纳机,根据不同的金额请求“尽量”吐出更多的金额代券,最高等值。问给定一个金额数量,和机器内不同面额代券的存,
量,问机器能吐出逼近请求的最高面值总和是多少。
题目模型很明显,很容易联系到多重背包。对于第一题,首先将弹珠价值总和减半表示为v,然后构造一个v容量背包,题目等价于询问
该背包的最大存入方案是否能使存入总重量恰好等于v?转移方程: dp[i][v]=Max( dp[i-1][v], dp[i-1][v-k*w[j]]+k*c[i] | w[i] = c[i], 0<=k<=maxK )。
同理第二题的转移方程与1014基本一样,物品收益即为物重,dp结果即为v容背包最大置入量。
题目数据很弱,据说硬搜能过。。。 不过还是按照多重背包的优化技巧来做,即用二进制拆分物品数,容易证明这样的拆分能保证最优性
和正确性。时间复杂度O(C*V*logN)。 对于O(C*V)算法,可以参考 《国家集训队2009论文集浅谈几类背包题 》中单调队列优化DP的技巧。
1014:
1#include<cstdio>
2#include<cstdlib>
3#include<cstring>
4#define max(a,b) (a>b)?a:b;
5#define MAXV 20000*6+1
6int dp[MAXV];
7int nk[6];
8
9int main(){
10 int i,k,min,v;
11 int cnt,sum;
12 int _case=0;
13 while(1){
14 for(sum=i=0;i<6;i++) {
15 scanf("%d",&nk[i]);
16 sum+=nk[i]*(i+1);
17 }
18 if( !nk[0] & !nk[1] & !nk[2] & !nk[3] & !nk[4] & !nk[5] ) break;
19 _case++;
20 if( _case!=1 ) printf("\n");
21 if( sum%2 ){
22 printf("Collection #%d:\n",_case);
23 printf("Can't be divided.\n");
24 }
25 else{
26 sum/=2;
27 memset(dp,0,sizeof(int)*(sum+1));
28 for(i=0;i<6;i++){
29 cnt=1;
30 k=(sum/(i+1)>=nk[i])?nk[i]:sum/(i+1);
31 while(k){
32 if( cnt>k ) cnt=k;
33 k-=cnt;
34 min=cnt*(i+1);
35 for(v=sum;v>=min;v--)
36 /**//* 对每一个拆分数 cnt,单独做 0/1背包 */
37 dp[v]=max(dp[v],dp[v-cnt*(i+1)]+cnt*(i+1));
38 cnt<<=1;
39 }
40 }
41 if( sum == p[sum] ) {
42 printf("Collection #%d:\n",_case);
43 printf("Can be divided.\n");
44 }
45 else {
46 printf("Collection #%d:\n",_case);
47 printf("Can't be divided.\n");
48 }
49 }
50 }
51 return 0;
52}
53