总结一下 按照:http://blog.csdn.net/libin56842/article/details/9338841 这个博客 提供的题号(感谢大牛) 和自己多加的几道01背包的题目。
其状态转移方程是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
01背包的裸代码:(节省一维空间)
for(int i=0;i<n;i++)
for(int j=V;j>=weight[i];j--)
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
1.HDU2602:Bone Collector
01背包裸题
http://acm.hdu.edu.cn/showproblem.php?pid=2602
#include<cstring>
#include<climits>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<stack>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
int dp[maxn],v[maxn],w[maxn];
int main()
{
int T;cin>>T;
while(T--){
memset(dp,0,sizeof(dp));
int n,W;cin>>n>>W;
for(int i=0;i<n;i++) cin>>v[i];
for(int i=0;i<n;i++) cin>>w[i];
for(int i=0;i<n;i++)
for(int j=W;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
cout<<dp[W]<<endl;
}
}
2.hdu2546 :饭卡
http://acm.hdu.edu.cn/showproblem.php?pid=2546
很经典的一道01背包题,要注意的是这里只要剩余的钱不低于5元,就可以购买任何一件物品,所以5在这道题中是很特许的,再使用01背包之前,我们首先要在现在所拥有的余额中保留5元,用这五元去购买最贵的物品,而剩下的钱就是背包的总容量,可以随意使用,因此可得代码.
#include<bits/stdc++.h>
using namespace std;
int dp[51010],n,m,a[1010];
int main()
{
while(cin>>n&&n)
{
memset(dp,0,sizeof(dp));
memset(a,0,sizeof(a));
for(int i=0;i<n;i++) cin>>a[i];
cin>>m;
if(m<5){
cout<<m<<endl;
continue;
}
m-=5;
sort(a,a+n);
for(int i=0;i<n-1;i++)
for(int j=m;j>=a[i];j--)
dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
cout<<m-a[n-1]-dp[m]+5<<endl;
}
}
3.HDU1171:Big Event in HDU
http://acm.hdu.edu.cn/showproblem.php?pid=1171
题意:01背包的变形,给你物品和种类,分成平均两部分,两部分分之差最小。先以sum/2去选择最多质量的物品,求得结果小于等于另一半。
#include<cstring>
#include<climits>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<stack>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int dp[255010],val[5010];
int main()
{
int n;
while(cin>>n){
memset(dp,0,sizeof(dp));
if(n<=0) break;
int sum=0,t=0;
for(int i=0;i<n;i++){
int a,b; cin>>a>>b;
sum+=a*b;
for(int j=0;j<b;j++)
val[t++]=a;
}
for(int i=0;i<t;i++)
for(int j=sum/2;j>=val[i];j--)
dp[j]=max(dp[j],dp[j-val[i]]+val[i]);
cout<<max(sum-dp[sum/2],dp[sum/2])<<" "<<min(sum-dp[sum/2],dp[sum/2])<<endl;
}
}
4.HDU2639:Bone Collector II(01背包第k优解)
http://acm.hdu.edu.cn/showproblem.php?pid=2639
题意:这题不是01背包最优解,而是第k优解
思路:这里加一维 ,每个状态下第k解,dp[n][k],打个比方,一个年级有几个班,你要知道年级第一,你就需要知道每个班第一,你要知道年级前k名,你就要知道每个班前k名,所以这里,求出每个决策k个解,在排序后转移。
#include<bits/stdc++.h>
using namespace std;
int dp[1010][50],value[110],cost[110],A[50],B[50];
int main()
{
int T;cin>>T;
while(T--){
memset(dp,0,sizeof(dp));
int n,v,k; cin>>n>>v>>k;
for(int i=0;i<n;i++) cin>>value[i];
for(int i=0;i<n;i++) cin>>cost[i];
for(int i=0;i<n;i++)
{
for(int j=v;j>=cost[i];j--)
{
for(int kk=0;kk<k;kk++)
{
A[kk]=dp[j-cost[i]][kk]+value[i];
B[kk]=dp[j][kk];
}
A[k]=-1,B[k]=-1;
int a=0,b=0,c=0;
while(c<k&&(a<k||b<k))
{
if(A[a]>B[b]) dp[j][c]=A[a],a++;
else dp[j][c]=B[b],b++;
if(dp[j][c]!=dp[j][c-1]) c++;
}
}
}
cout<<dp[v][k-1]<<endl;
}
}
5.HDU2955:Robberies
http://acm.hdu.edu.cn/showproblem.php?pid=2955
题意:这题带了点概率的知识,实质还是01背包选择问题,要求不被抓的概率小于一个范围,多个独立事件的概率等于他们的积,注意题目输入是被抓的总概率,和每个银行被抓的概率,注意取反。
定义dp[sum]抢到金额为sum不被抓的概率.
找到概率大于等于题目要求最大值。
#include<bits/stdc++.h>
using namespace std;
int v[110];
double dp[10010],p[110];
int main()
{
int T;cin>>T;
while(T--)
{
int sum=0;
double P,N;cin>>P>>N;
P=1-P;
for(int i=0;i<N;i++)
{
cin>>v[i]>>p[i];
sum+=v[i];
p[i]=1-p[i];
}
memset(dp,0,sizeof(dp));
dp[0]=1.0;
for(int i=0;i<N;i++)
for(int j=sum;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]*p[i]);
for(int i=sum;i>=0;i--)
{
if(dp[i]-P>0.00000001){
cout<<i<<endl;
break;
}
}
}
}
6.HDU3466:Proud Merchants
http://acm.hdu.edu.cn/showproblem.php?pid=3466
题意:买东西,每个东西有三个特征值,p代表价格,q代表你手中钱必须不低于q才能买这个物品,v代表得到的价值。
mark:又是变种01背包,每做一个变种的,就是一种提高。。
这题因为涉及到q,所以不能直接就01背包了。因为如果一个物品是5 9,一个物品是5 6,对第一个进行背包的时候只有dp[9],dp[10],…,dp[m],再对第二个进行背包的时候,如果是普通的,应该会借用前面的dp[8],dp[7]之类的,但是现在这些值都是0,所以会导致结果出错。
于是要想到只有后面要用的值前面都可以得到,那么才不会出错。设A:p1,q1 B:p2,q2,如果先A后B,则至少需要p1+q2的容量,如果先B后A,至少需要p2+q1的容量,那么就是p1+q2 > p2+q1,变形之后就是q1-p1 < q2-p2。
#include<bits/stdc++.h>
using namespace std;
struct node
{
int p,q,v;
}a[555];
int cmp(node a,node b)
{
return a.q-a.p<b.q-b.p;
}
int dp[5555];
int main()
{
#ifdef yxj
freopen("F:\\in.txt","r",stdin);
#endif // yxj
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=0;i<n;i++) scanf("%d %d %d",&a[i].p,&a[i].q,&a[i].v);
sort(a,a+n,cmp);
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
for(int j=m;j>=a[i].q;j--)
dp[j]=max(dp[j],dp[j-a[i].p]+a[i].v);
printf("%d\n",dp[m]);
}
}
7.HDU1864:最大报销额
http://acm.hdu.edu.cn/showproblem.php?pid=1864
题目中药注意的有几样,首先每张发票中单件物品价格不能超过600,其次发票总额不能超过1000,而且发票上的物品必须是ABC三类,将满足以上条件的发票存入数组之中,就是裸01背包
#include <fstream>
#include <iostream>
#include <string>
#include <complex>
#include <math.h>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <stdio.h>
#include <stack>
#include <algorithm>
#include <list>
#include <ctime>
#include <memory.h>
#include <ctime>
#include <assert.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define eps 1e-8
#define M_PI 3.141592653589793
typedef long long ll;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
using namespace std;
const int N=3000010;
int dp[N];
int p[50];
int main()
{
#ifdef yxj
freopen("F:\\in.txt","r",stdin);
#endif // yxj
double q;
int n;
while(cin>>q>>n&&n)
{
int cnt=0,Q=(int)(q*100);
for(int i=0;i<n;i++)
{
int t,a=0,b=0,c=0,flag=0;
scanf("%d",&t);
for(int j=0;j<t;j++)
{
char C; double num;
scanf(" %c:%lf",&C,&num);
// cout<<c<<" "<<num<<endl;
if(C=='A') a+=num*100;
else if(C=='B') b+=num*100;
else if(C=='C') c+=num*100;
else flag=1;
if(a+b+c>100000||a>60000||b>60000||c>60000) flag=1;
}
// cout<<a+b+c<<endl;
//cout<<"flag="<<flag<<endl;
if(flag) continue;
else p[cnt++]=a+b+c;
}
// cout<<cnt<<endl;
memset(dp,0,sizeof(dp));
//rep(i,0,cnt) cout<<p[i]<<endl;
for(int i=0;i<cnt;i++)
{
for(int j=Q;j>=p[i];j--)
dp[j]=max(dp[j],dp[j-p[i]]+p[i]);
}
printf("%.2f\n",dp[Q]/100.0);
}
}
8.世界。hrbust 2252
http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=2252
题意:完全背包,每个勇气(背包)有花费,和勇气值(价值),每个勇气数量无限,给你一个目标勇气值,问最小的花费。就是类似告诉你 总价值,求背包最小容量。
正常求前i个背包里,花费多少得到勇气最大值。再从小往上找第一个勇气值大于等于目标值的花费,并输出,按照这个dp这里花费上界得确定找一个最大的作为上届。
还可以 定义,前i个到达勇气值j的最小值。
#include <fstream>
#include <iostream>
#include <string>
#include <complex>
#include <math.h>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <stdio.h>
#include <stack>
#include <algorithm>
#include <list>
#include <ctime>
#include <memory.h>
#include <ctime>
#include <assert.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define eps 1e-8
#define M_PI 3.141592653589793
typedef long long ll;
const ll mod=1000000007;
const int inf=99999999;
ll powmod(ll a,ll b) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
using namespace std;
int cost[110],w[110];
/*
int main()
{
int T;cin>>T;
while(T--){
int n,m,sum=0;
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>cost[i]>>w[i];
sum=max(sum,((m/w[i])+1)*cost[i]);
}
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
for(int j=cost[i];j<=sum;j++)
dp[j]=max(dp[j],dp[j-cost[i]]+w[i]);
for(int i=1;i<=sum;i++)
if(dp[i]>=m)
{
cout<<i<<endl;
break;
}
}
}
*/
int dp[2100];//勇气值为m的最小花费.
int main()
{
int T;cin>>T;
while(T--){
int n,m,maxx=0;cin>>n>>m;
for(int i=0;i<n;i++)
cin>>cost[i]>>w[i],maxx=max(maxx,w[i]);
for(int i=0;i<=m+maxx;i++) dp[i]=inf;
dp[0]=0;
for(int i=0;i<n;i++)
for(int j=w[i];j<=m+maxx;j++)
dp[j]=min(dp[j],dp[j-w[i]]+cost[i]);
int res=inf;
for(int i=m+maxx;i>=m;i--)
{
//cout<<dp[i]<<endl;
res=min(res,dp[i]);
}
cout<<res<<endl;
}
}