2754题意:给定M(1<=M<=200)以及长度为M的四个数组,分别记为Pairs、Multi、Low、Up,你需要构造一个长度为M的数组Table(其中Low[i]<=Table[i]<=Up[i]),满足∑Multi[i]*Table[i] = 0,且使得∑Pairs[i]*Table[i]尽量大。输入满足至少有一个解。
思路:如果直接上dp,那么复杂度最大可达M*极差*50。其中M是序列长度,50是因为每个数最多有50个取值的可能,极差是∑Multi[i]*Table[i]能达到的最大值和最小值之间的差值。无法承受。下面考虑将其转化为多重背包问题。
将下界单独拿出来作为一部分进行计算,因此[Low[i],Up[i]]就转化为[0, U[i]-L[i]]的一个多重背包。M[i]和P[i]均进行单独的下界计算。计算出 T = L[1]*M[1]+L[2]*M[2].... 之后, 就是一个关于容量T刚好放满的多重背包。
多重背包有一个二进制优化,参见(http://www.cnblogs.com/favourmeng/archive/2012/09/07/2675580.html)。
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#define N 205
#define ORI 50000
#define INF 0x7fffffff
using namespace std;
int dp[200005];
int n,m;
int up[N],low[N],p[N],w[N];
int main(){
while(scanf("%d",&n) != EOF){
m = 0;
int presum = 0;
for(int i = 0;i<n;i++){
scanf("%d %d %d %d",&p[i],&w[i],&low[i],&up[i]);
m += w[i]*low[i];
presum += p[i]*low[i];
up[i] -= low[i];
}
m = -m;
for(int i = 1;i<=m;i++)
dp[i] = -INF;
dp[0] = 0;
for(int i = 0;i<n;i++){
int num = 1;
while(up[i] > num){
for(int j = m;j>=num*w[i];j--)
dp[j] = max(dp[j], dp[j-num*w[i]]+num*p[i]);
up[i] -= num;
num <<= 1;
}
for(int j = m;j>=up[i]*w[i];j--)
dp[j] = max(dp[j], dp[j-w[i]*up[i]]+p[i]*up[i]);
}
printf("%d\n",dp[m]+presum);
}
return 0;
}
1014题意:有六种石头,分别价值1~6,现在给出价值分别为1~6的石头的数量(石头总数不超过20000)。问能否把这些石头分成两类,使得每类的价值总和相等。
思路:首先求价值总和,如果是奇数必然不能。否则就变成了一个多重背包问题,看看能否凑成总和的一半,用二进制优化。
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <vector>
#include <queue>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <iostream>
#define N 120005
#define INF 0x3fffffff
int s[7],c = 1;
bool dp[N>>1];
using namespace std;
int main(){
while(1){
int m = 0;
for(int i = 1;i<=6;i++){
scanf("%d",&s[i]);
m += i*s[i];
}
if(m == 0)
break;
if(m & 1)
printf("Collection #%d:\nCan't be divided.\n\n",c++);
else{
memset(dp,false,sizeof(dp));
dp[0] = true;
m >>= 1;
for(int i = 1;i<=6;i++){
if(!s[i])
continue;
int num = 1;
while(s[i] > num){
for(int j = m;j-num*i>=0;--j)
dp[j] |= dp[j - num*i];
s[i] -= num;
num <<= 1;
}
for(int j = m; j - s[i]*i>=0; --j)
dp[j] |= dp[j - s[i]*i];
}
if(dp[m])
printf("Collection #%d:\nCan be divided.\n\n",c++);
else
printf("Collection #%d:\nCan't be divided.\n\n",c++);
}
}
return 0;
}