1. 程式人生 > >POJ-1742-Coins-優化的多重揹包

POJ-1742-Coins-優化的多重揹包

題目傳送門
題意:
有n種硬幣,這n種硬幣的價值為coin[i].val,第i種硬幣的個數為coin[i].num個,問能用這些硬幣支付多少不超過m的價格?
思路:
多重揹包裸題,但是要進行空間和時間上的優化,否則會MLE和TLE。
空間優化:通過特定的順序滾動陣列,來降維。具體可以參考揹包九講
時間優化:一般的多重揹包有3重迴圈,i,j,k-種類,價值,數量。這裡優化掉數量那一重迴圈。具體細節看程式碼註釋。

AC code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<cstring>
#include<string>
#include<cmath>

using namespace std;

typedef long long LL;

#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define mem(a,b) memset(a,b,sizeof(a))
#define per(i,a,b) for(int i = a;i <= b;++i)
#define rep(i,a,b) for(int i = a;i >= b;--i)
const int maxn = 1e5;
int n = 0,m = 0;
struct node{
	int val,num;
};
node coin[maxn+10];
bool dp[maxn+10];//不能用二維陣列,那樣會MLE,要滾動降維
int cnt[maxn+10]; 
bool cmp(node a,node b){
	return a.val != b.val ? a.val < b.val : a.num < b.num;
}
void solve(){
	fill(dp,dp+(maxn+10),false);
	dp[0] = true;
	per(i,1,n){
		fill(cnt,cnt+maxn+10,0);
		per(j,0,m){//正序,因為是從更新後的狀態退出後面的j的狀態的 
			if(dp[j] == true){//不要跳過,否則cnt會+1,導致過快選擇的i種卡超過數量限制 
				continue;//為了保證使用最少的第i種卡,湊出j 
			}
			if(j >= coin[i].val && dp[j-coin[i].val] && 
			cnt[j - coin[i].val] < coin[i].num){
				dp[j] = dp[j - coin[i].val];
				cnt[j] = cnt[j-coin[i].val] + 1;
			} 
		}
	}
	int ans = 0;
	per(i,1,m){
		ans += dp[i] ? 1 : 0; 
	}
	printf("%d\n",ans);
}
int main(){
	while(~scanf("%d %d",&n,&m) && ( m +n )){
		per(i,1,n){
			scanf("%d",&coin[i].val);
		}
		per(i,1,n){
			scanf("%d",&coin[i].num);
		}
		//sort(coin+1,coin+1+n,cmp);//排序是多餘的 
		solve();
	}
	return 0;
}