题解:
这道题和杭电上的2844是一样的,但是poj上面的数据可能要强一些。在杭电上可能模板多重背包就可以过,但是这里就要变通一下,今天再做到,配合着杭电2844,把这道题的做法好好整理了一下,一共三种方法dp。
方法一:多重背包记录状态
因为题目只要求得到有多少个可达,所以可以用bool表示dp数组,变化递推式,最后压线AC。
递推式变化为 dp[i] |= dp[i-cost];
代码实现:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MAX_N = 110;
const int MAX_M = 100010;
int N,M;
int res;
int A[MAX_N];
int C[MAX_N];
bool dp[MAX_M];
void ZeroOnePack(int cost,int weight);
void CompletePack(int cost,int weight);
void MultiplePack(int cost,int weight,int amount);
int main(){
while( scanf("%d%d",&N,&M) != EOF ){
if( N == 0 && M == 0 ){
break;
}
for( int i = 0; i < N; i++ ){
scanf("%d",&A[i]);
}
for( int i = 0; i < N; i++ ){
scanf("%d",&C[i]);
}
res = 0;
memset(dp,0,sizeof(dp));
dp[0] = 1;
for( int i = 0; i < N; i++ ){
MultiplePack(A[i],A[i],C[i]);
}
for( int i = 1; i <= M; i++ ){
//表示容量为i的背包装满
if( dp[i] ){
res++;
}
}
printf("%d\n",res);
}
return 0;
}
void MultiplePack(int cost,int weight,int amount){
if( cost*amount >= M ){
CompletePack(cost,weight);
return ;
}
int k = 1;
while( k < amount ){
ZeroOnePack(k*cost,k*weight);
amount -= k;
k *= 2;
}
ZeroOnePack(amount*cost,amount*weight);
return ;
}
void CompletePack(int cost,int weight){
for( int i = cost; i <= M; i++ ){
dp[i] |= dp[i-cost];
//dp[i] = max(dp[i],dp[i-cost]+cost);
}
return ;
}
void ZeroOnePack(int cost,int weight){
for( int i = M; i >= cost; i-- ){
dp[i] |= dp[i-cost];
//dp[i] = max(dp[i],dp[i-cost]+cost);
}
return ;
}
方法二:记录可达状态,dp钱币剩余数目
钱币i从0~N-1遍历,对于每一种钱币,若在这之前dp[j]已经可达,赋值dp[j] = C[i],然后记录剩余数目dp,排除不可达的情况。
代码实现:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MAX_M = 100010;
const int MAX_N = 110;
int N,M;
int res;
int A[MAX_N];
int C[MAX_N];
int dp[MAX_M];
int main(){
while( scanf("%d%d",&N,&M) != EOF ){
if( N == 0 && M == 0 ){
break;
}
for( int i = 0; i < N; i++ ){
scanf("%d",&A[i]);
}
for( int i = 0; i < N; i++ ){
scanf("%d",&C[i]);
}
memset(dp,-1,sizeof(dp));
dp[0] = 0;
res = 0;
for( int i = 0; i < N; i++ ){
for( int j = 0; j <= M; j++ ){
if( dp[j] >= 0 ){
dp[j] = C[i];
}
else if( j < A[i] || dp[j-A[i]] < 0 ){
dp[j] = -1;
}
else{
dp[j] = dp[j-A[i]]-1;
}
}
}
for( int i = 1; i <= M; i++ ){
if( dp[i] >= 0 ){
res++;
}
}
printf("%d\n",res);
}
return 0;
}
方法三:记录可达状态,对于每一种钱币dp使用数目
设置vis数组,每次更新dp数组,在之前可达的基础上dp,dp数组记录使用数目。
代码实现:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MAX_M = 100010;
const int MAX_N = 110;
int N,M;
int res;
int A[MAX_N];
int C[MAX_N];
int dp[MAX_M];
int vis[MAX_M];
int main(){
while( scanf("%d%d",&N,&M) != EOF ){
if( N == 0 && M == 0 ){
break;
}
for( int i = 0; i < N; i++ ){
scanf("%d",&A[i]);
}
for( int i = 0; i < N; i++ ){
scanf("%d",&C[i]);
}
memset(vis,0,sizeof(vis));
vis[0] = 1;res = 0;
for( int i = 0; i < N; i++ ){
memset(dp,0,sizeof(dp));
for( int j = A[i]; j <= M; j++ ){
if( vis[j-A[i]] && !vis[j] ){
if( dp[j-A[i]] < C[i] ){
res++;
dp[j] = dp[j-A[i]]+1;
vis[j] = 1;
}
}
}
}
printf("%d\n",res);
}
return 0;
}