Google Kickstart2019 Round B Problem B能量石
链接:https://www.acwing.com/problem/content/736/
类似玩杂质的牛与国王游戏
如果最终方案中相邻的两石头为
l[i]/s[i]<l[j]/s[j]
l[i]*s[j]<s[i]*l[j]
则这两石头的收益和
ei-(s[1]+…s[i-1])*li+ej-(s[1]+…s[i])*lj
如果交换它们
ej-(s[1]+…s[i-1])*lj+max(0,ei-(s[1]+…+s[i-1]+s[j])*li)
上面两式最后可以整理成
获得s[j]*l[i]
获得s[i]*l[j]
则矛盾,故最终答案一定是L[i]/s[i]>=L[j]/S[j],
移项L[i]*S[j]>=L[j]*S[i]
如果为相等的情况,是否会出现先完成后面的再完成前面的情况,
类似上面的推理,可以发现交换完之后答案不会变坏,故把所有石头按这个规则排完序,最终答案一定可以通过这个顺序dp得到。直接dp即可。
#include<bits/stdc++.h>
using namespace std;
const int N=105;
struct arr{
int s,e,l;
}a[N];
int T,n,f[N][N*N];
int cmp(arr A,arr B){
return A.s*B.l<A.l*B.s;
}
int main(){
cin>>T;
for(int t=1;t<=T;t++){
cin>>n;
int m=0;
for(int i=1;i<=n;i++)cin>>a[i].s>>a[i].e>>a[i].l,m+=a[i].s;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
f[i][j]=f[i-1][j];
if(j>=a[i].s){
int pro=a[i].e-(j-a[i].s)*a[i].l;
if(pro>0)f[i][j]=max(f[i][j],f[i-1][j-a[i].s]+pro);
}
}
}
int ans=0;
for(int i=0;i<=m;i++)ans=max(ans,f[n][i]);
printf("Case #%d: %d\n",t, ans);
}
return 0;
}
、
2967 [USACO09DEC]Video Game Troubles G
链接:https://www.luogu.com.cn/problem/P2967
对于每个游戏机,可以单独考虑
每次将前i-1个游戏机单独拷贝出来,然后在现有基础上把i这个游戏机加上,对于每个游戏进行背包dp,之后与前面的进行更新即可。
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,money,n1,x,y;
int f[N],g[N];
int main(){
cin>>n>>m;
while(n--){
cin>>money>>n1;
for(int i=money;i<=m;i++)g[i]=f[i-money];
while(n1--){
cin>>x>>y;
for(int j=m;j>=money+x;j--)g[j]=max(g[j],g[j-x]+y);
}
for(int i=0;i<=m;i++)f[i]=max(f[i],g[i]);
}
cout<<f[m]<<endl;
return 0;
}
有依赖的背包问题
链接:https://www.acwing.com/problem/content/10/
树上背包dp,f[i][j]表示以i为根的子树选j个点最大为多少。
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,v,a[N],b[N],root,x;
vector<int>e[N];
int f[N][N];
void dfs(int x){
for(int i=0;i<e[x].size();i++){
dfs(e[x][i]);
for(int j=v-a[x];j>=0;j--){
for(int k=0;k<=j;k++){
f[x][j]=max(f[x][j],f[x][j-k]+f[e[x][i]][k]);
}
}
}
for(int i=v;i>=a[x];i--)f[x][i]=f[x][i-a[x]]+b[x];
for(int i=0;i<a[x];i++)f[x][i]=0;
}
int main(){
cin>>n>>v;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>x;
if(x==-1)root=i;
else e[x].push_back(i);
}
dfs(root);
cout<<f[root][v]<<endl;
return 0;
}
1776 宝物筛选
链接:https://www.luogu.com.cn/problem/P1776
单调队列优化多重背包
#include<bits/stdc++.h>
using namespace std;
const int N=40005;
int n,m,v,w,s,h,t,q[N];
int f[N],g[N];
int main(){
cin>>n>>m;
while(n--){
cin>>w>>v>>s;
memcpy(g,f,sizeof(f));
for(int i=0;i<v;i++){
h=0;t=-1;
for(int j=i;j<=m;j+=v){
if(h<=t&&(j-q[h])/v>s)h++;
while(h<=t&&g[q[t]]+(j-q[t])/v*w<=g[j])t--;
q[++t]=j;
f[j]=g[q[h]]+(j-q[h])/v*w;
}
}
}
cout<<f[m]<<endl;
return 0;
}