PAT(甲级)渡劫(二十六)-Find More Coins (30)
算法思想:
题意:从n个硬皮中选取方案,使得总和价值正好为m,如果有多种,方案为排列最小的那个。
可以把硬币看成w=v(即容量=价值)的物品,现在要选取这些物品放入到容量为m的背包中,求能装的最大价值。
如果最大价值恰好等于容量m,那么方案则是可行的,否则输出No Solution。
由于要输出排列最小的方案,所以先将硬币按价值从大到小排列,相当于我先装大的,再装小的。
接着用01背包的方法求dp[j]。
chosen[i][j]
数组表示背包里容量为j的时候,是否含有第i个物品,用于最后序列的输出。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 10000+4;
const int maxv = 105;
int coins[maxn];
int dp[maxv]; // dp[v]表示:总和加起来=v的序列中最后一个值最小为多少。
int chosen[maxn][maxv]; // choosen[i][j]数组表示背包里容量为j时,是否含有第i个物品,用于最后序列的输出
int ans[maxn];
int cnt = 0;
bool cmp(int a,int b){
return a > b;
}
int main(){
//freopen("in.txt","r",stdin);
int n,m;
scanf("%d %d",&n,&m);
for(int i = 0 ; i < n ; i++){
scanf("%d",&coins[i]);
}
sort(coins,coins+n,cmp); // 按从大到小的顺序排序
dp[0] = 0; // 边界
for(int i = 0 ; i < n ; i++){
for(int j = m ; j >= coins[i] ; j--){
if(dp[j] <= dp[j-coins[i]]+coins[i]){
dp[j] = dp[j-coins[i]]+coins[i];
chosen[i][j] = 1;
}
}
}
if(dp[m] != m){
printf("No Solution");
}else{
int v = m,idx = n-1;
while(v){
if(chosen[idx][v]){
ans[cnt++] = coins[idx];
v-=coins[idx];
}
idx--;
}
printf("%d",ans[0]);
for(int i = 1 ; i < cnt ; i++)
printf(" %d",ans[i]);
}
return 0;
}