背包DP

P2340 [USACO03FALL] Cow Exhibition G

  • 题意:

\(N\) 头奶牛,每头奶牛有智商 \(S\) 与情商 \(F\),选出一些奶牛使得它们的情商之和和智商之和大于 \(0\) 且情商智商总和最大。

  • 思路:

我们发现,跟状态有关的三个值:\(S\) , \(F\) , \(S+F\),我们只需要知道其中的两个就可以推出剩下一个,所以,我们可以选其中一个做体积,一个做价值,跑一遍 \(01\)背包即可。

#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
inline ll rd(){
	ll x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	} 
	while(ch>='0'&&ch<='9'){
		x=1ll*x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int n;
struct cow{
	int a,b;
}c[450];
ll sum[1800002];
void init(){
	n=rd();
	for(int i=1;i<=n;i++) c[i].a=rd(),c[i].b=rd();
}
void solve(){
    memset(sum,-0x3f,sizeof sum);
    sum[400000]=0;
	for(int i=1;i<=n;i++){
		if(c[i].a>=0){
            for(int j=800000;j>=c[i].a;j--){
                sum[j]=max(sum[j],sum[j-c[i].a]+c[i].b);
            }
        }
        else{
            for(int j=0;j<=800000+c[i].a;j++){
                sum[j]=max(sum[j],sum[j-c[i].a]+c[i].b);
            }
        }
	} 
}
void print(){
	ll maxx=-1e8;
	for(int i=400000;i<=800000;i++)
        if(sum[i]>=0)
            maxx=max(maxx,sum[i]+i-400000);
	cout<<maxx;
}
signed main(){
	init();
	solve();
	print();
}

P3188 [HNOI2007] 梦幻岛宝珠

  • 题意:

\(n\) 颗宝石,选一些宝石使它们总重量不超过 \(W\) 且价值和最大,重量 \(w\) 不超过 \(2^{30}\) 且可以表示为 \(a*2^b\)

  • 思路:

一看是 \(01\) 背包板子,但问题就出在数据范围,看到 \(w\) 可以表示为 \(a*2^b\) 就从这里入手,设 \(f[i][j]\) 表示从重量为 \(k*2^i\) 物品 (\(k\) 为系数),选出了体积为 \(j\) 的物品,但此时我们发现,我们还需要一个数组来帮助我们,于是再定义一个 \(g\) 数组,\(g[i][j]\) 表示已经从 \(j*2^i\) 从中选取的最大价值,且前 \(i-1\) 位已经选完。那么我们发现我们可以枚举 \(p\) 表示从 \(2^i\) 里选 \(p\) 体积,那么代表剩下的 \(j-p\) 就到了 \(2^{(i-1)}\) 这里,就成了 \(2*(j-p)\) ,所以状态转移方程就为:

\[g[i][j]=\max(g[i][j],g[i-1][2*(j-p)+((W>>(i-1))\&1)]+f[i][p]) \]

关于 \(((W>>(i-1))\&1)\) 因为 \(W\)\(i-1\) 位上可能原本为 \(1\) ,那样的话需要加上

#include<bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
const int N=105;
int n,W;
struct dn{
	int a,b,len,x;
}z[N];
int f[50][12000],g[50][12000];
vector<int> val[51],k[51];
void init(int n){
	memset(f,0,sizeof f);
	memset(g,0,sizeof g);
	for(int i=0;i<=50;i++) val[i].clear(),k[i].clear();
	for(int i=1;i<=n;i++){
		scanf("%d%d",&z[i].a,&z[i].b);
		z[i].len=0;
		while(((z[i].a>>z[i].len)&1)==0){
			z[i].len++;
		}
		z[i].x=z[i].a;
		val[z[i].len].push_back(z[i].b);
		k[z[i].len].push_back((z[i].x>>z[i].len));
	}
	
	int l=0;
	while((W>>l)) l++;
	l--;
	for(int i=0;i<=l;i++){
		if(k[i].size()==0) continue;
		else{
			for(int j=0;j<k[i].size();j++){
				for(int p=1000;p>=k[i][j];p--){
					f[i][p]=max(f[i][p],f[i][p-k[i][j]]+val[i][j]);
				}
			}
		}
	}
	for(int i=0;i<=l;i++){
		for(int j=1000;j>=0;j--){
			for(int p=0;p<=j;p++){
				if(i==0) g[i][j]=max(g[i][j],f[i][p]);
				else{
					g[i][j]=max(g[i][j],g[i-1][(j-p)*2+((W>>(i-1)&1))]+f[i][p]);
				}
			}
		}
	}
	cout<<g[l][1]<<"\n";
}
signed main(){
	while(cin>>n>>W){
		if(n==-1&&W==-1) break; 
		init(n);
	}
} 

P4138 [JOISC2014] 挂饰

  • 题意:

一开始有一个挂钩,现在有 \(n\) 个物品,每个物品又有 \(A_i\) 个挂钩和 \(B_i\) 的喜悦度,现在想要最大化所有挂饰的喜悦值之和。

  • 思路:
    首先按照 \(B_i\) 由大到小排个序,把 \(A_i\) 当作重量,\(B_i\) 当价值,跑遍 \(01\) 背包即可
#include<bits/stdc++.h>
using namespace std;
int n;
struct noe{
	int a,b;
}z[4005];
bool cmp(noe x,noe y){
	return x.a>y.a;
}
int f[4003][4003];
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%d%d",&z[i].a,&z[i].b);	
	}	
	sort(z+1,z+1+n,cmp);
	for(int i=0;i<=n;i++) f[0][i]=-1e9,f[i][n+1]=-1e9;
	f[0][1]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=n;j++){
			f[i][j]=max(f[i-1][j],f[i-1][max(j-z[i].a,0)+1]+z[i].b);
		}
	}
	int ans=-1e9;
	for(int i=0;i<=n;i++) ans=max(ans,f[n][i]);
	cout<<ans;
}
posted @ 2025-01-23 18:53  Distant_Cloud  阅读(11)  评论(0)    收藏  举报