题解 P4025 [PA2014]Bohater

题目中没有说怪物掉落的血药 ai 和消耗的生命值 di 的大小关系,所以我们可以把怪物分为两类:补血怪(diai)与掉血怪(di>ai)。

容易发现的是,一定是先打完补血怪再打掉血怪。因为打掉血怪没有益处,反而减少生命值。所以我们把它分为两部分,第一部分是打补血怪,第二部分是打掉血怪。

第一部分,在打每个补血怪时的要求是 z>di。由于 z 会逐渐变大,所以应当先打 di 小的怪物。

第二部分,在打每个掉血怪时的要求是 z>di。但是 z 会逐渐变小,我们不能先打 di 小/大的怪物。怎么办?我们考虑倒序,计算打完所有怪物后的剩余生命值,从最后开始考虑。每一个怪物相当于让 z 先减去 ai 再加上 di。条件变成了 z>ai。由于是倒序,z 会逐渐增大,所以倒序按 ai 从小到大打就行了。调整回正序就是按照 ai 从大到小。

感觉有异曲同工之妙的题目

以下是部分代码(几乎全部)。

#define ll long long
const int N=100005;
ll n,z;
struct mon
{
	ll d,a;
	int id;
}a[N],b[N],c;
int cnta,cntb;
bool cmp1(mon x,mon y)
{
	return x.a<y.a;
}
bool cmp2(mon x,mon y)
{
	return x.d<y.d;
}
int main()
{
	n=read(),z=read();
	ll p=z;
	for (int i=1;i<=n;i++) 
	{
		c.d=read(),c.a=read(),c.id=i;
		if (c.d>c.a) b[++cntb]=c;
		else a[++cnta]=c;
		z-=c.d,z+=c.a;
	}
	sort(a+1,a+cnta+1,cmp2);
	sort(b+1,b+cntb+1,cmp1);
	for (int i=1;i<=cntb;i++) 
	{
		if (z>b[i].a) z+=b[i].d-b[i].a;
		else 
		{
			puts("NIE");
			return 0;
		}
	}
	z=p;
	for (int i=1;i<=cnta;i++) 
	{
		if (z>a[i].d) z+=-a[i].d+a[i].a;
		else 
		{
			puts("NIE");
			return 0;
		}
	}
	puts("TAK");
	for (int i=1;i<=cnta;i++) cout << a[i].id << " ";
	for (int i=cntb;i>=1;i--) cout << b[i].id << " ";
	return 0;
}
posted @   Little09  阅读(41)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示