Google Kickstart2019 Round B Problem B
problem:
解题思路
一种较优秀的解法(贪心 + 背包dp)
假设最优解的能量石排列长度为k(1<=k<=n)k(1<=k<=n) 因为去掉了那些没有贡献的宝石,位置为:
a1,a2,a3…aka1,a2,a3…ak。
那么对于任意两个位置i=al,j=al+1(1<=l<k)i=al,j=al+1(1<=l<k)
交换后两个宝石的贡献总和不会变得更大,即(假设之前的总时间为tt ):
Ei−t∗Li+Ej−(t+Si)∗Lj>=Ej−t∗Lj+Ei−(t+Sj)∗LiEi−t∗Li+Ej−(t+Si)∗Lj>=Ej−t∗Lj+Ei−(t+Sj)∗Li
整理后:
Si∗Lj<=Sj∗LiSi∗Lj<=Sj∗Li。
我们可以把跟ii有关的放到一边,调整一下:
SiLi<=SjLjSiLi<=SjLj
这样,我们只要以如上条件作为宝石间排序的条件,进行一次sortsort。
因为对于其他形式的放置规律,必然可以通过交换满足SiLi>SjLjSiLi>SjLj的相邻的两项来得到更小值。
那么最优解的坐标(新的坐标)一定满足:
ai<a2<a3…<ak
这时候我们再用01背包的方式求解
代码如下
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=110;
struct stone{
int s,e,l;
bool operator<(const stone &W)const{
return s*W.l<l*W.s;
}
}stone[N];
int f[N];
int main(){
int T;
cin>>T;
for(int c=1;c<=T;c++){
int n;
cin>>n;
int m=0;
for(int i=0;i<n;i++){
int s,e,l;
cin>>s>>e>>l;
stone[i]={s,e,l};
m+=s;
}
sort(stone,stone+n);
memset(f,0,sizeof f);
for(int i=0;i<n;i++)
{
int s=stone[i].s,e=stone[i].e,l=stone[i].l;
for(int j=m;j>=s;j--)
f[j]=max(f[j],f[j-s]+e-(j-s)*l);
}
int res=0;
for(int i=0;i<=m;i++) res=max(res,f[i]);
printf("Case #%d: %d\n",c,res);
}
return 0;
}