专栏文章

题解:P14205 [ROI 2016 Day1] 要塞防御

P14205题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miniivlc
此快照首次捕获于
2025/12/02 02:58
3 个月前
此快照最后确认于
2025/12/02 02:58
3 个月前
查看原文

题号:P14205

题目链接:P14205 [ROI 2016 Day1] 要塞防御

题目简述:

题目一共给我们 nn 个防御段,每个防御段一名士兵可以抵御的敌人数量 kik_i 和每个防御段会有的敌人数量 aia_i以及我们的士兵总量 ss。要我们通过合理分配士兵,求出最少会有多少名敌人突破防线。

思路:

这道题很显然,我们优先选择让士兵去 kik_i 更大的防御段是最优解,因为这时一名士兵所能抵御的敌人数量就更多一些。可这时,还有一个问题:
假如我们有一个会有 33 名敌人进攻的防御段,这个防御段一名士兵可以抵御 22 名敌人。那么,我们派一个人去这个防御段,这个士兵可以抵御 22 名敌人,可如果我们再派一个人去,就只能再让他抵御剩下的 11 名敌人了。
这个问题启发我们要处理最后 11 名士兵所能解决的敌人数量不是 kik_i 的情况。
我们选择用 map 来记录一名士兵可以抵御的敌人数量,再从大到小选取。最后让敌人总数减去我们抵御住的敌人总数就可以了。

代码:

CPP
#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T&x){
	x=0;char c;int sign=1;
	do{c=getchar();if(c=='-') sign=-1;} while(!isdigit(c));
	do{x=x*10+c-'0';c=getchar();}while(isdigit(c));
	x*=sign;
}//快读增加输入效率
long long n,s;
long long a[100005],k[100005];
long long d[200005],l;
map<long long,long long> f;
bool cmp(long long s1,long long s2){
	return s1>s2;
}//从大到小排序
long long z;
int main(){
	read(n),read(s);
	for(int i=1;i<=n;i++){
		read(a[i]),read(k[i]);
		z+=a[i];//计算敌人总数
		d[++l]=k[i],d[++l]=a[i]%k[i];
		f[k[i]]+=a[i]/k[i];//记录一名士兵可以抵御k_i名敌人的数量
		f[a[i]%k[i]]++;//记录一名士兵可以抵御a_i % k_i名敌人的数量
	}
	sort(d+1,d+l+1,cmp);//贪心排序
	for(int i=1;i<=l;i++){
		if(d[i]==d[i-1]) continue;//避免重复选择
		if(f[d[i]]<=s) z-=f[d[i]]*d[i],s-=f[d[i]];//计算答案
		else z-=d[i]*s,s=0;//计算答案
	}
	cout<<z;//输出答案
	return 0;
}

总结:

本题利用 贪心 思想,总体难度不算太高,适合用作 贪心 初学者的练习题。

评论

0 条评论,欢迎与作者交流。

正在加载评论...