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


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

题目大意

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

1-4 测试点 20分

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

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\le2\),那么只有一种攻击情况,就是 \(r=2\) 的卡牌战胜 \(r=1\) 的。所以只要统计 \(r=2\) 的数量 \(r_2\),和 \(r=1\) 的数量 \(r_1\)
如果 \(r_1\le r_2\) ,那么所有的 \(r_1\) 都会被击败,答案为 \(r_2\)
如果 \(r_1\gt r_2\) ,那么 \(r_2\) 当中全部存活,\(r_1\) 当中会存活下来 \(r_1-r_2\) ,答案为 \(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} \]

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-10^5\),那么也就说明几乎所有的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\le2\) 的情况就很类似了。处理过的卡牌我们看作是r=1的卡牌,正在处理的卡牌,我们看作是r=2的卡牌。情况也是类似的

\[存活数=\begin{cases} r2 &r_1\le r_2 &所有处理过的卡牌r_1都被击败了,只剩下当前正在处理的r_2\\ r1 &r_1\gt r_2 &当前的卡牌r_2击败了一部分处理过的r_1,但存活总数不变\\ \end{cases} \]

复杂的思路可以由简单的思路拓展而来,这里也体现出来了我们从小的测试点入手的好处,可以给我们提供思路,帮助我们解题。

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;
}
posted @ 2025-04-27 17:37  ChenyangDu  阅读(114)  评论(0)    收藏  举报