饭卡:
#include<algorithm>
using namespace std;
int dp[10010];
int b[10010];
int main()
{
int a;
while (~scanf("%d", &a) && a )
{
memset(dp, 0, sizeof(dp));
memset(b, 0, sizeof(b));
for (int i = 0; i < a; i++)
{
scanf("%d", &b[i]);
}
sort(b, b + a);
int k;
scanf("%d", &k);
if (k < 5)
{
printf("%d\n", k);
}
else
{
for (int i = 0; i < a-1; i++)
{
for (int j = k - 5; j >= b[i]; j--)
{
dp[j] = max(dp[j], dp[j - b[i]] + b[i]);
}
}
printf("%d\n", k - dp[k - 5] - b[a - 1]);
}
}
}
思路:首先,可以负债,所以我们给费用排序之后取出最贵的用来负债,然后dp。这里唯一要注意的就是反向dp,也就是这里 for (int j = k - 5; j >= b[i]; j--)。为什么不能正向,因为同一种东西不能拿两次。我们假设有一个东西要5块钱,如果正着算,那么dp[5]就会有一个值,然后到了dp[10]的时候,你会等于dp[5]+5,而这时很明显你已经是取了买了这个东西了,但是dp[5]你已经给它买过了,这里就等于5+5.所以就不对了。
换零钱:
#include<iostream>
#include<cstdio>
using namespace std;
int a[100005],dp[100005];
int main()
{
int b[]={1,2,5,10,20,50,100,200,500,1000,2000,5000,10000};
int n,i,j;
cin>>n;
dp[0]=1;
for(j=0;j<13;j++) {
for(i=b[j];i<=n;i++)
dp[i]=( dp[i-b[j]] + dp[i] )%1000000007;
}
cout<<dp[n]<<endl;
return 0;
}
方程设置原因:每一次刷新得到的种类就是 (本身有的钱减去上一次花掉的钱的种类) 加上原有的种类。循环往复。
Adventurer's Guild :
#include<algorithm>
using namespace std;
long long h[2000], s[2002], con[2002],dp[310][310];
int main()
{
long long a, b, c;
scanf("%lld%lld%lld", &a, &b, &c);
for (int i = 1; i <= a; i++)
{
scanf("%lld %lld %lld", &h[i], &s[i], &con[i]);
}
for (int i = 1; i <= a; i++)
{
for (int j = b; j >= h[i]; j--)
{
for (int k = c; k >= 0; k--)
{
if (s[i] > k)
{
long long f = j + k - s[i];
if (f >= h[i])
{
dp[j][k] = max(dp[j][k], dp[f - h[i]][0] + con[i]);
}
}
else
{
dp[j][k] = max(dp[j][k], dp[j - h[i]][k - s[i]] + con[i]);
}
}
}
}
printf("%lld", dp[b - 1][c]);
}
存在两个量改变:二维背包。注意事项:反向循环,状态转移。且题中要求生命值不为0,所以输出dp[b-1][c]即可。记得long long。