L3-001 凑零钱 (30 分)——01背包+路径记录

题目

思路

  • 凑出价值为 m m m的零钱等价于从 v [ i ] = w [ i ] v[i]=w[i] v[i]=w[i] 的01背包中凑出花费了体积为 m m m,最大价值为 m m m 的方案。
  • 我们将零钱金额从大到小排序,那么如果存在相同的方案,后来的字典序一定比之前的字典序小,所以直接更新即可。

AC代码

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x; i<=y; ++i)
#define per(i,x,y) for(int i=x; i>=y; --i)
#define pushk push_back
#define popk pop_back
#define mem(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define ll long long
#define lp p<<1
#define rp p<<1|1
#define emk emplace_back
using namespace std;
const int N = 1e4+9;
const int M = 1e2+9;
int f[N][M];
bool st[N][M];
int v[N],w[M]; 
vector<int> ve[108];
int main() {
	
	int n,m;
	cin>>n>>m;
	rep(i,1,n) cin>>v[i];
    //我们从大到小排序,那么01背包更新路径的时候,新来的字典序一定比之前的字典序小
	sort(v+1,v+n+1,greater<int>());
	rep(i,1,n){
		rep(j,v[i],m){
			f[i][j]=f[i-1][j];
			if(f[i-1][j-v[i]]+v[i]>=f[i][j]){
				f[i][j]=f[i-1][j-v[i]]+v[i];
				st[i][j]=1;	
			}
		}
	}
	if(f[n][m]==m){
		int i=n,j=m;
		vector<int> ans;
		while(i>=1 && j>=0){
			if(st[i][j]){
				ans.pushk(v[i]);
				j-=v[i];
			}
			i--;
		}
		rep(i,0,(int)ans.size()-1) {
			if(i) cout<<" ";
			cout<<ans[i];
		}
	}
	else{
		puts("No Solution"); 
	}
	return 0;
}

posted @ 2022-08-28 08:42  翔村亲亲鸟  阅读(12)  评论(0编辑  收藏  举报