有个背包,背包有重量上限,有一些不同的骨头,骨头有价值和重量,选一些骨头放进背包,求最大价值。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1010;
const int maxm=1010;
int f[maxm];
struct obj{
int w,v;
} a[maxn];
int n,V;
int T;
int main(){
scanf("%d",&T);
while(T--){
memset(f,0,sizeof(f));
scanf("%d%d",&n,&V);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].v);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].w);
for(int i=1;i<=n;i++)
for(int j=V;j>=a[i].w;j--)
f[j]=max(f[j],f[j-a[i].w]+a[i].v);
printf("%d\n",f[V]);
}
}
每个f[i]维护一个序列保存其前k优解
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=110;
const int maxv=1010;
const int maxk=35;
int f[maxv][maxk];
struct obj{
int v,w;
} a[maxn];
int n,W,T,k;
int x[maxn],y[maxn];
int main(){
scanf("%d",&T);
while(T--){
memset(f,0,sizeof(f));
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
scanf("%d%d%d",&n,&W,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].v);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].w);
for(int i=1;i<=n;i++){
for(int j=W;j>=a[i].w;j--){
for(int l=1;l<=k;l++){
x[l]=f[j-a[i].w][l]+a[i].v;
y[l]=f[j][l];
}
for(int p1=1,p2=1,l=1;l<=k&&(p1<=k||p2<=k);){
if(x[p1]>y[p2])
f[j][l]=x[p1++];
else
f[j][l]=y[p2++];
if(f[j][l]!=f[j][l-1])l++;
}
}
}
printf("%d\n",f[W][k]);
}
}
用5元买(抢)掉最贵的,剩下的做01背包
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1010;
const int maxv=1010;
int f[maxv];
int w[maxn];
int n,m;
int T;
int main(){
while(scanf("%d",&n)&&n){
memset(w,0,sizeof(w));
for(int i=n;i>=1;i--){
scanf("%d",&w[i]);
if(w[i]>w[n])swap(w[i],w[n]);
}
scanf("%d",&m);
if(m<5){
printf("%d\n",m);
continue;
}
memset(f,0,sizeof(f));
for(int i=1;i<n;i++){
for(int j=m-5;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+w[i]);
}
printf("%d\n",m-f[m-5]-w[n]);
}
}
f[i]表示付出i的代价收不到一份OFFER的概率,做01背包
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn=10010;
const int maxm=10010;
int w[maxm];
double v[maxm],f[maxn];
int n,m;
double min(double a,double b){
return a<b?a:b;
}
int main(){
while(~scanf("%d%d",&n,&m),n+m){
for(int i=1;i<=m;i++){
scanf("%d%lf",&w[i],&v[i]);
v[i]=1.0-v[i];
}
for(int i=0;i<=n;i++)f[i]=1.0;
for(int i=1;i<=m;i++)
for(int j=n;j>=w[i];j--)
f[j]=min(f[j],f[j-w[i]]*v[i]);
printf("%.1f%%\n",(1-f[n])*100);
}
}
f[i]表示抢i元之后能逃脱的概率,做01背包即可
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=110;
const int maxm=10010;
int w[maxn];
float f[maxm];
float v[maxn];
float p;
int n,m;
int T;
float max(float a,float b){
return a>b?a:b;
}
int main(){
scanf("%d",&T);
while(T--){
cin>>p>>n;
p=1-p;
m=0;
for(int i=1;i<=n;i++){
cin>>w[i]>>v[i];
v[i]=1-v[i];
m+=w[i];
}
memset(f,0,sizeof(f));
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=m;j>=w[i];j--){
f[j]=max(f[j],f[j-w[i]]*v[i]);
}
for(int i=m;i>=0;i--)
if(f[i]>p){
printf("%d\n",i);
break;
}
}
}
已知一些硬币的值和重量,现给出一个重量,求这些硬币可能的值的最小值
f[i]表示重量为i时的价值最小值,做完全背包即可
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=510;
const int maxm=10010;
const int INF=0x3f3f3f3f;
int T,m,n;
int f[maxm];
int w[maxn],v[maxn];
int main(){
scanf("%d",&T);
while(T--){
int p,q;
scanf("%d%d",&p,&q);
m=q-p;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&v[i],&w[i]);
for(int i=1;i<=m;i++)f[i]=INF;
f[0]=0;
for(int i=1;i<=n;i++)
for(int j=w[i];j<=m;j++)
f[j]=min(f[j],f[j-w[i]]+v[i]);
if(f[m]==INF)
puts("This is impossible.");
else
printf("The minimum amount of money in the piggy-bank is %d.\n",f[m]);
}
}
把每个物品的数量二进制拆分,然后做01背包
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1010;
const int maxm=2555555;
int w[maxn],num[maxn];
int f[maxm];
int n,m,T;
int main(){
while(scanf("%d",&n)){
if(n<0)return 0;
m=0;
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
scanf("%d%d",&w[i],&num[i]);
m+=w[i]*num[i];
}
for(int i=1;i<=n;i++){
int left=num[i];
for(int k=1;k <= left;k<<=1){
left-=k;
int t=w[i]*k;
for(int j=m/2;j>=t;j--){
f[j]=max(f[j],f[j-t]+t);
}
}
if(left>0){
int t=left*w[i];
for(int j=m/2;j>=t;j--)
f[j]=max(f[j],f[j-t]+t);
}
}
int ans1=max(f[m/2],m-f[m/2]);
int ans2=min(f[m/2],m-f[m/2]);
printf("%d %d\n",ans1,ans2);
}
}
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=110;
const int maxm=110;
int w[maxm],v[maxm],num[maxm];
int f[maxn];
int n,m,T;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&w[i],&v[i],&num[i]);
}
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
int left=num[i];
for(int k=1;k<=left;k<<=1){
int ww=w[i]*k;
int vv=v[i]*k;
left-=k;
for(int j=n;j>=ww;j--)
f[j]=max(f[j],f[j-ww]+vv);
}
if(left){
int ww=w[i]*left;
int vv=v[i]*left;
for(int j=n;j>=ww;j--)
f[j]=max(f[j],f[j-ww]+vv);
}
}
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,f[i]);
printf("%d\n",ans);
}
}
给出6种价值不同的物品,每种物品有若干个,问是否能分成总价值相等的两份。
首先总价值为奇数时不可。f[i]表示总价值不超过i时能获得的最大价值。判断f[m/2]==m-f[m/2]即可
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxm=120010;
int f[maxm];
int n,T=0,m;
int a[10];
bool read(){
T++;
m=0;
for(int i=1;i<=6;i++){
scanf("%d",&a[i]);
m+=a[i]*i;
}
return m==0?0:1;
}
void work(){
printf("Collection #%d:\n",T);
memset(f,0,sizeof(f));
for(int i=1;i<=6;i++){
int left=a[i];
if(left==0)continue;
for(int k=1;k<=left;k<<=1){
int t=i*k;
left-=k;
for(int j=m/2;j>=t;j--)
f[j]=max(f[j],f[j-t]+t);
}
if(left){
int t=i*left;
for(int j=m/2;j>=t;j--)
f[j]=max(f[j],f[j-t]+t);
}
}
if(f[m/2]==m-f[m/2])
puts("Can be divided.");
else
puts("Can't be divided.");
printf("\n");
}
int main(){
while(read()){
work();
}
}
给出一些硬币的价值和数量,问用这些硬币能组合出1~m中的哪些价值。
用f[i]表示价值i能否被组合出来即可。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=110;
const int maxm=100010;
bool f[maxm];
int a[maxn],c[maxn];
int n,m;
int main(){
while(scanf("%d%d",&n,&m)&&(n+m)){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
memset(f,0,sizeof(f));
f[0]=1;
for(int i=1;i<=n;i++){
int left=c[i];
for(int k=1;k<=left;k<<=1){
int t=a[i]*k;
left-=k;
for(int j=m;j>=t;j--)
f[j]|=f[j-t];
}
if(left){
int t=left*a[i];
for(int j=m;j>=t;j--)
f[j]|=f[j-t];
}
}
int ans=0;
for(int i=1;i<=m;i++)
if(f[i])ans++;
printf("%d\n",ans);
}
}
和一维没多大区别。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=110;
const int maxm=110;
int f[maxm][maxm];
int n,m,k,s;
int v[maxn],w[maxn];
int main(){
while(~scanf("%d%d%d%d",&n,&m,&k,&s)){
for(int i=1;i<=k;i++)
scanf("%d%d",&v[i],&w[i]);
memset(f,0,sizeof(f));
for(int i=1;i<=k;i++)
for(int j=w[i];j<=m;j++)
for(int l=1;l<=s;l++)
f[j][l]=max(f[j][l],f[j-w[i]][l-1]+v[i]);
if(f[m][s]<n){
puts("-1");
}else{
int cost=m;
for(int i=0;i<=m;i++)
for(int j=0;j<=s;j++)
if(f[i][j]>=n)cost=min(i,cost);
printf("%d\n",m-cost);
}
}
}
ACboy要上M天课,有N种课,每种课上不同天数会获得不同的愉悦度,问ACboy能获得的最大愉悦度。
分组背包上就好了。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=110;
const int maxm=10010;
int n,m;
int a[maxn][maxn];
int f[maxm];
int main(){
while(scanf("%d%d",&n,&m)&&n+m){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
for(int j=m;j>=1;j--)
for(int k=1;k<=j;k++)
f[j]=max(f[j],f[j-k]+a[i][k]);
printf("%d\n",f[m]);
}
}
有一些鞋属于不同组,每种鞋有一个价格和一个价值,现要用不超过M的钱在每组中买至少一种鞋,且每种鞋最多买一次,求所能获得的最大价值。
f[i,t]表示枚举到第i组鞋,价格之和为t时的最大价值。f[i,t]=max(f[i,t],f[i-1,t-s[i,j].w]+s[i,j].v,f[i,t-s[i,j].w]+s[i,j].v)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=110;
const int maxm=10010;
const int maxk=15;
struct obj{
int w,v;
};
int n,m,k;
int f[maxk][maxm];
int max(int a,int b,int c){
return max(a,max(b,c));
}
int main(){
while(~scanf("%d%d%d",&n,&m,&k)){
vector<obj> s[maxk];
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
s[a].push_back((obj){b,c});
}
for(int i=1;i<=k;i++)
for(int j=0;j<s[i].size();j++)
for(int t=m;t>=s[i][j].w;t--)
f[i][t]=max(f[i][t],
f[i-1][t-s[i][j].w]+s[i][j].v,
f[i][t-s[i][j].w]+s[i][j].v);
int ans=0;
for(int i=1;i<=m;i++)ans=max(ans,f[k][i]);
if(ans==0)puts("Impossible");
else printf("%d\n",ans);
}
}
f[u,m]表示在以i为根的子树中选了包括u在内的m个节点所获得的最大价值
f[u,1]=s[u]
f[u,j]=max(f[u,j],f[u,j-k]+f[v,k]) k
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=210;
int f[maxn][maxn];
int s[maxn];
vector<int> g[maxn];
int n,m;
void DFS(int u,int m){
int siz=g[u].size();
f[u][1]=s[u];
for(int i=0;i<siz;i++){
int v=g[u][i];
if(m-1>0)DFS(v,m-1);
for(int j=m;j>=2;j--)
for(int k=1;k<j;k++)//k<j保证选了u
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
}
}
int main(){
while(scanf("%d%d",&n,&m)&&n+m){
for(int i=0;i<=n;i++)
g[i].clear();
memset(f,0,sizeof(f));
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(i);
s[i]=b;
}
DFS(0,m+1);
printf("%d\n",f[0][m+1]);
}
}
给出树上每个节点的bugs数量和可获得能量数,现要在节点上驻扎士兵,每个士兵可以消灭20个bugs,节点上bugs少于20时要驻扎一个士兵。现在有m个士兵,问能获得的最大能量为多少。
f[i,j]表示以i为根的子树中驻扎j个士兵能获得的最大能量,在树上DP即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=110;
const int maxm=110;
int f[maxn][maxm];
int w[maxn],p[maxn];
bool vst[maxn];
int n,m;
vector<int> g[maxn];
void DFS(int u){
vst[u]=1;
for(int i=w[u];i<=m;i++)
f[u][i]=p[u];
int siz=(int)g[u].size();
for(int i=0;i<siz;i++){
int v=g[u][i];
if(vst[v])continue;
DFS(v);
for(int j=m;j>=w[u];j--)
for(int k=1;k<=j-w[u];k++)
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
}
}
int main(){
while(scanf("%d%d",&n,&m),n+m>0){
memset(f,0,sizeof(f));
memset(w,0,sizeof(w));
memset(p,0,sizeof(p));
memset(vst,0,sizeof(vst));
for(int i=1;i<=n;i++)g[i].clear();
for(int i=1;i<=n;i++){
scanf("%d%d",&w[i],&p[i]);
w[i]=(w[i]+19)/20;
}
for(int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
if(m==0){
puts("0");
continue;
}
DFS(1);
printf("%d\n",f[1][m]);
}
}