[CSP-S 2024] 决斗 逐个测试点详解,带你骗分


考场上如果想不到正解,不妨先写其中测试点的答案,一方面是先拿到一些分数,另一方面也可以脱开思路,有助于想到正确的解法。

题目大意

n张卡牌,i卡牌如果大于j卡牌,j被淘汰,i攻击成功,一个卡牌只能攻击一次,使得剩余的卡牌数量最多。

1-4 测试点 20分

n ≤ 10 n\le10 n10,可以全排列,所有攻击顺序的数量 n ! = 3628800 ≤ 1 0 7 n! =3628800\le 10^7 n!=3628800107 ,可以枚举所有攻击顺序,后一张卡牌攻击前一张,找到剩余卡牌最多的方案。

const int N = 1e5+5;
int arr[N];
int n;

int id[N];
int usd[N];
int ans;
void dfs(int a){
	if(a == n){
		// 递归结束 
		int res = n;
		for(int i=1;i<n;i++){
			if(arr[id[i]] > arr[id[i-1]]){
				res --;
			}
		}
		if(res < ans){
			ans = res;
		}
	}
	for(int i=0;i<n;i++){
		if(!usd[i]){
			usd[i] = 1;
			id[a] = i;
			dfs(a+1);
			
			usd[i] = 0;
		}
	}
}

void solve1(){
	ans = n;
	dfs(0);
	printf("%d\n",ans);
}

int main(){
	freopen("duel1.in","r",stdin);
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>arr[i];
	}
	if(n <= 10){
		solve1();
	}
	
	return 0;
}

5-10 测试点 30分

由于所有的 r ≤ 2 r\le2 r2,那么只有一种攻击情况,就是 r = 2 r=2 r=2 的卡牌战胜 r = 1 r=1 r=1 的。所以只要统计 r = 2 r=2 r=2 的数量 r 2 r_2 r2,和 r = 1 r=1 r=1 的数量 r 1 r_1 r1
如果 r 1 ≤ r 2 r_1\le r_2 r1r2 ,那么所有的 r 1 r_1 r1 都会被击败,答案为 r 2 r_2 r2
如果 r 1 > r 2 r_1\gt r_2 r1>r2 ,那么 r 2 r_2 r2 当中全部存活, r 1 r_1 r1 当中会存活下来 r 1 − r 2 r_1-r_2 r1r2 ,答案为 r 1 − r 2 + r 2 = r 1 r_1-r_2+r_2=r_1 r1r2+r2=r1
存活数 = { r 2 r 1 ≤ r 2 所有的 r 1 都被击败了,只剩下 r 2 r 1 r 1 > r 2 r 2 击败了一部分 r 1 ,但存活总数不变 存活数=\begin{cases} r2 &r_1\le r_2 &所有的r_1都被击败了,只剩下r_2\\ r1 &r_1\gt r_2 &r_2击败了一部分r_1,但存活总数不变\\ \end{cases} 存活数={r2r1r1r2r1>r2所有的r1都被击败了,只剩下r2r2击败了一部分r1,但存活总数不变

const int N = 1e5+5;
int arr[N];
int n;

// 5-10 测试点 
void solve2(){
	int r1=0,r2=0;
	for(int i=0;i<n;i++){
		if(arr[i] == 1)r1++;
		else r2++;
	}
	if(r1 <= r2)printf("%d\n",r2);
	else printf("%d\n",r1);
}

int main(){
	cin>>n;
	int maxR = 0;
	for(int i=0;i<n;i++){
		cin>>arr[i];
		if(arr[i] > maxR) maxR = arr[i];
	}
    if(maxR <= 2){
		solve2();
	}
	
	return 0;
}

11-15 测试点 20分

这里n只有30个,但所有的r是随机分布在 1 − 1 0 5 1-10^5 1105,那么也就说明几乎所有的r都不相同,那直接输出1就可以了,只有最大的一个会存活下来,其他的都会被淘汰。
这应该是最简单的20分了,代码量也极少,只需要用一个set来判断是否有重复的情况即可。

const int N = 1e5+5;
int arr[N];
int n;

// 11-15测试点
void solve3(){
	printf("%d\n",1);
}

int main(){
	cin>>n;
	set<int> se;
	bool all_one = true;
	for(int i=0;i<n;i++){
		cin>>arr[i];
		if(se.find(arr[i]) != se.end()){
			all_one = false;
		}
		se.insert(arr[i]);
	}
	if(all_one){
		solve3();
	}
	return 0;
}

16-20 测试点 30分

首先将所有卡牌进行从小到大排序,每次处理一批数值相同的卡牌,当前正在处理的卡牌一定比处理过的卡牌数值大。那这和 r ≤ 2 r\le2 r2 的情况就很类似了。处理过的卡牌我们看作是r=1的卡牌,正在处理的卡牌,我们看作是r=2的卡牌。情况也是类似的
存活数 = { r 2 r 1 ≤ r 2 所有处理过的卡牌 r 1 都被击败了,只剩下当前正在处理的 r 2 r 1 r 1 > r 2 当前的卡牌 r 2 击败了一部分处理过的 r 1 ,但存活总数不变 存活数=\begin{cases} r2 &r_1\le r_2 &所有处理过的卡牌r_1都被击败了,只剩下当前正在处理的r_2\\ r1 &r_1\gt r_2 &当前的卡牌r_2击败了一部分处理过的r_1,但存活总数不变\\ \end{cases} 存活数={r2r1r1r2r1>r2所有处理过的卡牌r1都被击败了,只剩下当前正在处理的r2当前的卡牌r2击败了一部分处理过的r1,但存活总数不变
复杂的思路可以由简单的思路拓展而来,这里也体现出来了我们从小的测试点入手的好处,可以给我们提供思路,帮助我们解题。

const int N = 1e5+5;
int arr[N];
int n;

void solve4(){
	sort(arr,arr+n);
	int r1 = 0,r2 = 0,max;
    for(int i=0;i<=n;i++){
    	if(i == 0 || i == n || arr[i] > max){
    		if(r1 <= r2) r1 = r2;
    		max = arr[i];
    		r2=0;
    	}
    	r2++;
    }
    printf("%d\n",r1);
}
 
int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>arr[i];
	}
	solve4();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值