#4631 疯狂的火神

23 篇文章 0 订阅
16 篇文章 0 订阅
描述

火神为了检验 zone 的力量,他决定单挑 n n n 个人。

由于火神训练时间有限,最多只有 t t t 分钟,所以他可以选择一部分人来单挑, 由于有小 y 的帮助,他得到了每个人特定的价值,每个人的价值由一个三元组 ( a , b , c ) (a,b,c) (a,b,c)组成,表示如果火神在第 x 分钟单挑这个人,他就会得到 a − b ∗ x a-b*x abx 的经验值, 并且他需要 c c c 分钟来打倒这个人。

现在火神想知道,他最多可以得到多少经验值,由于火神本来就很笨,进入 zone 的疯狂的火神就更笨了,所以他希望你来帮他计算出他最多可以得到多少 经验值。

输入

第一行一个正整数 T T T,表示数据组数。

对于每组数据,第一行为两个正整数 n n n t t t,表示跟火神单挑的人的个数和 火神的训练时间。下面 n n n 行,每行三个正整数 A i , B i , C i Ai,Bi,Ci Ai,Bi,Ci,表示每个人的价值,含义见题目。

输出

对于每组数据输出一行一个整数,表示火神最多能得到多少经验值。

样例输入
1
4 10
110 5 9
30 2 1
80 4 8
50 3 2
样例输出
88
提示
  • 对于 20%的数据满足: 1 ≤ n ≤ 10 1≤n≤10 1n10
  • 对于 50%的数据满足: 1 ≤ n ≤ 18 1≤n≤18 1n18
  • 对于 100%的数据满足: 1 ≤ n ≤ 1000 , 1 ≤ t ≤ 3000 , 1 ≤ C i ≤ t , A i ≤ 106 1≤n≤1000,1≤t≤3000,1≤Ci≤t,Ai≤106 1n1000,1t3000,1Cit,Ai106
  • 保证 n > 200 n>200 n>200 的数据组数不超过 5 5 5 组,其他的数据组数不超过 10 10 10
  • 保证每个人贡献的经验值到训练结束都不会变成负数
解析:

可以看出,该题的原型是 01 01 01背包问题

首先可知并不是所有的人都要选入被挑战的队伍,则算法第一步便是按某种优先级排序:
对于两个人 i i i j j j谁先被挑战选其中更优的方案,设之前的选择都已达到最优,即挑战两人前所花的时间相同,又因为都要挑战 i i i j j j,所以结束对两人的挑战时所花时间也相同,设 t t t为挑战两人前则只需要判断 a [ i ] − b [ i ] ∗ ( c [ i ] + t ) + a [ j ] − b [ j ] ∗ ( t + c [ i ] + c [ j ] ) a[i]-b[i]*(c[i]+t)+a[j]-b[j]*(t+c[i]+c[j]) a[i]b[i](c[i]+t)+a[j]b[j](t+c[i]+c[j]) a [ j ] − b [ j ] ∗ ( c [ j ] + t ) + a [ i ] − b [ i ] ∗ ( t + c [ j ] + c [ i ] ) a[j]-b[j]*(c[j]+t)+a[i]-b[i]*(t+c[j]+c[i]) a[j]b[j](c[j]+t)+a[i]b[i](t+c[j]+c[i])整理一下,就是判断 − b [ j ] ∗ c [ i ] - b[j] * c[i] b[j]c[i] − b [ i ] ∗ c [ j ] - b[i] * c[j] b[i]c[j]的大小,取绝对值小的更优
排完序后再跑一遍 01 D P 01DP 01DP就可以了

CODE
#include<bits/stdc++.h>
using namespace std;
int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-f;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=(s<<3)+(s<<1)+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N=3e3+5;
struct fjy{
	int a,b,c;
}l[205];
 
int f[N],vis[N],cz[N];
int m,n,t,ans;
bool com(const fjy &x,const fjy &y){
	return x.c<y.c;
}
int max(int x,int y){
	return x>y?x:y;
}
int main(){
//	freopen("crazy.in","r",stdin);
//	freopen("crazy.out","w",stdout);
	m=read();
	while(m--){
		//queue<int> q;//记录需要处理的时间点
		memset(cz,0,sizeof cz);
		memset(f,0,sizeof f);
		memset(vis,0,sizeof vis);
		n=read(),t=read();
		for(int i=1;i<=n;i++){
			l[i].a=read(),l[i].b=read(),l[i].c=read();
		}
			/*sort(l+1,l+n+1,com);*/
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n-1;j++)
				if(l[j].b*l[j+1].c<l[j].c*l[j+1].b) swap(l[j],l[j+1]);
		/*for(int i=1;i<=n;i++)
			if(l[i].a-l[i].b*l[i].c>f[l[i].c])
			{
				if(!vis[l[i].c]) q.push(l[i].c);
				f[l[i].c]=l[i].a-l[i].b*l[i].c;
				cz[l[i].c]=i;
			}
		while(!q.empty()){
			int x=q.front();q.pop();vis[x]=0;
			for(int i=cz[x]+1;i<=n;i++)
				if(f[cz[x]]+l[i].a-l[i].b*(l[i].c+cz[x])>f[l[i].c+cz[x]])
				{
					f[l[i].c+cz[x]]=f[cz[x]]+l[i].a-l[i].b*(l[i].c+cz[x]);
					if(!vis[l[i].c+cz[x]]) q.push(l[i].c+cz[x]);
					cz[l[i].c+cz[x]]=i;
				}
		}*/
		for(int i=1;i<=n;i++){//按照优先顺序枚举 
			for(int j=t;j>=l[i].c;j--) f[j]=max(f[j],f[j-l[i].c]+l[i].a-l[i].b*j);//01背包只能装一次,若从小到大,可能被装入过多次
		} 
		for(int i=t;i>0;i--) if(ans<f[i]) ans=f[i];
		printf("%d\n",ans);ans=0;
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值