背包的変式还真多啊,这一次又被困扰主了,后来翻了翻背包九讲,发现原来求最优解
总数能用dp解决,然后....
题目大意:某人有m(0<=m<=500)块钱,现在有n(1<=n<=30)个纪念品,每个纪念品要ti( ti<=500)
块钱,然后问,最多能买多少个纪念品,同时选择又有多少.
思路:普通的01背包就不讲了.来讲讲求和那回事把
首先讲一下G[i][j]的定义:对第i个物品决策后的最优解方案个数.
状态转移方程:
if(dp[j - v[i]]+1>dp[j]){
dp[j] = dp[j - v[i]] + 1;
G[j] = G[j - v[i]];
}
else if(dp[j - v[i]]+1==dp[j]){
G[j] += G[j - v[i]];
}
也就是说G[i][j]的值来三种情况:
(1)更新G[i][j] = G[i-1][j-v[i]];
(2)增加G[i][j] = G[i-1][j] + G[i-1][j-v[i]];
(3)不变G[i][j] = G[i-1][j];
以下时AC代码;
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <ctime>
#include <cmath>
using namespace std;
const int INF = 0x7fffffff;
int dp[505];
int G[505];
int v[35];
int main()
{
//freopen("/home/user/桌面/in","r",stdin);
int ca;
scanf("%d",&ca);
while(ca--){
memset(dp,0,sizeof(dp));
memset(G,0,sizeof(G));
dp[0] = 0;
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&v[i]);
G[0]=1;
for(int i=1;i<=n;++i){
for(int j=m;j>=v[i];--j){
if(dp[j - v[i]]+1>dp[j]){
dp[j] = dp[j - v[i]] + 1;
G[j] = G[j - v[i]];
}
else if(dp[j - v[i]]+1==dp[j]){
G[j] += G[j - v[i]];
}
}
}
// for(int i=0;i<=m;++i){
// cout<<" G["<<i<<"]:"<<G[i]<<" dp["<<i<<"]:"<<dp[i]<<endl;
// }
int mk=dp[m],se=0;
for(int i=0;i<=m;++i){
if(dp[i] == mk){
se += G[i];
}
}
if(dp[m] > 0)printf("You have %d selection(s) to buy with %d kind(s) of souvenirs.\n",se,dp[m]);
else printf("Sorry, you can't buy anything.\n");
}
//printf("time=%.3lf",(double)clock()/CLOCKS_PER_SEC);
return 0;
}