背包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;
}