题目链接:https://vjudge.net/problem/POJ-1742
转自:https://www.cnblogs.com/sevenun/p/5442279.html
题意:有n种硬币及其数量,求凑出的价值不超过m的方案数。
思路:设d[i][j]——前i种硬币,凑成总值j时,第i种硬币所剩余的个数。
默认d[i][j] = -1,代表无法凑成总值j
转移方程为,若d[i-1][j]≥0,代表前i-1种已能够凑成j,那么就不必花费第i种硬币,所以d[i][j] = c[i]
否则就看d[i][j-a[i]]的值,显然如果j < a[i],那么d[i][j] = -1,否则d[i][j-a[i]] ≤ 0,代表此刻第i种硬币已使用完了,所以自然d[i][j] = -1;
否则,d[i][j] = d[i][j-a[i]]-1;
可以看到d[i][]的值只与d[i-1][]和d[i][]有关,所以我们可以采用一维数组的形式,从而能够节省内存空间。
当然也可以用异或进行空间优化不用滚动数组,便于理解。
#include <iostream>
#include <cstring>
using namespace std;
int v[106],w[105],dp[100005];
int main()
{//设d[i][j]——前i种硬币,凑成总值j时,第i种硬币所剩余的个数.
ios::sync_with_stdio(false);
int n,m;
while( cin>>n>>m)
{
if(n==m&&n==0)
break;
for(int i=0; i<n; i++)
{
cin>>v[i];
}
for(int i=0; i<n; i++)
{
cin>>w[i];
}
memset(dp,-1,sizeof(dp));
dp[0]=0;
for(int i=0; i<n; i++)
{
for(int j=0; j<=m; j++)
{
if(dp[j]>=0)
{
dp[j]=w[i];
}
else if(j<v[i]||dp[j-v[i]]<=0)
{
dp[j]=-1;
}
else
{
dp[j]=dp[j-v[i]]-1;
}
}
}
int ans=0;
for(int i=1; i<=m; i++)
{
if(dp[i]>=0)
ans++;
}
cout<<ans<<endl;
}
return 0;
}
异或优化
#include <iostream>
#include <cstring>
using namespace std;
int v[106],w[105],dp[2][100005];
int main()
{//设d[i][j]——前i种硬币,凑成总值j时,第i种硬币所剩余的个数.
ios::sync_with_stdio(false);
int n,m;
while( cin>>n>>m)
{
if(n==m&&n==0)
break;
for(int i=0; i<n; i++)
{
cin>>v[i];
}
for(int i=0; i<n; i++)
{
cin>>w[i];
}
memset(dp,-1,sizeof(dp));
dp[0][0]=0;
dp[1][0]=0;
int id=1;
for(int i=0; i<n; i++)
{
for(int j=0; j<=m; j++)
{
if(dp[id^1][j]>=0)
{
dp[id][j]=w[i];
}
else if(j<v[i]||dp[id][j-v[i]]<=0)
{
dp[id][j]=-1;
}
else
{
dp[id][j]=dp[id][j-v[i]]-1;
}
}
id^=1;
}
int ans=0;
for(int i=1; i<=m; i++)
{
if(dp[id^1][i]>=0)
ans++;
}
cout<<ans<<endl;
}
return 0;
}