韩梅梅喜欢满宇宙到处逛街。现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债。韩梅梅手边有 10000 枚来自各个星球的硬币,需要请你帮她盘算一下,是否可能精确凑出要付的款额。
输入格式:
输入第一行给出两个正整数:N(≤10000 )是硬币的总个M(≤100 )是韩梅梅要付的款额。第二行给出 N 枚硬币的正整数面值。数字间以空格分隔。输出格式:
在一行中输出硬币的面值 V1 ≤V2≤⋯≤Vk,满足条件 V1 +V2+…+Vk=M。数字间以 1 个空格分隔,行首尾不得有多余空格。若解不唯一,则输出最小序列。若无解,则输出 No Solution。注:我们说序列{ A[1],A[2],⋯ }比{ B[1],B[2],⋯ }“小”,是指存在 k≥1 使得 A[i]=B[i] 对所有 i
输入样例 1:
8 9
5 9 8 7 2 3 4 1
输出样例 1:
1 3 5
输入样例 2:
4 8
7 2 4 3
输出样例 2:
No Solution
很明显这是一个可行性dp(至少我是这么认为的)
我们只要一个一个考虑 看可以凑出那些面额的硬币就行了
//n为硬币个数
//m为可以价值
//dp[i][j]代表考虑前i个硬币时是否可以凑出来j价值(dp[i][j]的值为0和1,1代表可以0代表不行)
for(int i=0;i<=n;i++)//初始化边界条件 0价值当然是可以了赋值1也是为了考虑某块硬币的价值恰好是j
{ //没看懂数据没关系看下面
dp[i][0]=1;
}
for(int i=1;i<=n;i++)//遍历每一块硬币
{
for(int j=1;j<=m;j++)//遍历每一种价值
{
if(dp[i-1][j-cost[i]]==1&&j-cost[i]>=0)//如果前i-1块硬币可以拼出j-cost[i]
dp[i][j]=1; //那么就把第i块硬币加进去就可以拼出来了j
//j-cost[i]>=0是防止越界
else dp[i][j]=dp[i-1][j];//否则就继承i-1的情况可能dp[i-1][j]就是1
}
}
上面代码看懂了 就解决了3分之一
现在他们要的是输出序列
if(dp[n][m]==1)
{
while(m)
{
if(dp[n-1][m-cost[n]==1)//首先前提是dp[n][m]==1然后考虑前n-1块硬币时可以凑出m-cost[n]
{ //那么dp[n][m]就是有前dp[n-1][m-cost[n]]凑出来的
m-=cost[n]; //找到了一块硬币了 那么我们就要找dp[n-1][m-cost[n]]的方案是啥了
ans[k++]=cost[n];//记录一下 为了以后输出
}
n--;//如果每找到也不用气馁 一定找得到 因为为什么是这样算过来的 第n块不行那么肯定到前面
}
}
还没有完!!!!
多种方案
又要序列小那我们就排降序 然后找输出序列的时候就会先考虑小面值硬币是不是凑成m的一个部分
于是有
#include
#include
#include
#include
using namespace std;
int cost[10005];
int dp[10005][101];
int ans[10005];
int k=0;
bool compare(int a,int b)
{
return a>b;
}
int main()
{
int n;
int m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>cost[i];
}
sort(cost+1,cost+n+1,compare);
for(int i=0;i<=n;i++)
{
dp[i][0]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(dp[i-1][j-cost[i]]==1&&j-cost[i]>=0)
dp[i][j]=1;
else dp[i][j]=dp[i-1][j];
}
}
if(dp[n][m]==1)
{
while(m)
{
if(dp[n-1][m-cost[n]]==1)//因为dp[n][m]为1 要是不考虑第n块硬币可以出拼出来m-cost[n]那么 //dp[n][m]方案就是由dp[n-1][m-cost[n]]的方案配上第n这块硬币
{ //要考虑m由哪个产生 那么求出dp[n-1][
ans[k++]=cost[n];
m-=cost[n];
}
n--;
}
for(int i=0;i