wtpavssite

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

构造

1.Permutation Swapping

来源:https://ac.nowcoder.com/acm/contest/129582/C

开始的思路:

我们可以观察到,如果这个数字的总数大于3,那么我们无论无何都可以对其排序到只剩三个数字,左边的已经全部排序好了,而,只剩三个数字的情况下无论无何都可以排序到左边全部排序好,右边只剩两个数字,而只剩两个数字是一定可以排序成功的。

错误的原因:

以上我在比赛的时候已经想到了,可惜的是我的特殊情况只包含了总共含有三个数字的情况,对其中第二个是不是2进行特殊判断,但实际上还有两个特殊情况,一个是总共两个数字和总共一个数字的时候,可惜这两个情况我没有考虑到,就没写出来。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
void solve(){
	int n;
	cin>>n;
	vector<int>a(n+1);
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	if(n>3)
		cout<<"YES"<<endl;
	else if(n==3){
		if(a[2]==2)
			cout<<"YES"<<endl;
		else
			cout<<"NO"<<endl;
	}else if(n==2){
		if(a[1]!=1)
			cout<<"NO"<<endl;
		else 
			cout<<"YES"<<endl;
	}else if(n==1)
		cout<<"YES"<<endl;
	
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
}

2.小红的中位数

来源:https://ac.nowcoder.com/acm/contest/130107/D

错误的思路:

最开始我考虑贪心的思想,我发现如果一个数列是偶数个,那么删除了一个元素之后,新的中位数会在原来的中位数的位置的后面一个,而奇数则是在前面一个。这个时候我觉得奇数长度的数列应该尽量删除当前的中位数或者当前中位数的位置的后面的数字,不然新的中位数将会是原来的中位数,这就浪费了一次操作机会,那么偶数的时候也是这样尽量删除中位数或者中位数前面的数字。然后在某种程度上我认为每一次都删除当前位置的中位数是最优的。然后可以观察到一个数列的数量在每次删除的操作之后奇偶性会发生变化,那么我认为最佳的删除顺序就是在最开始的中位数的位置上向两边循环删除相等的数字,那么寻找左右两边相等数字的半径,分成两边半径大小情况不同的情况来讨论,再结合我推导出的奇数和偶数情况不同的讨论就能给出答案

错误的原因:

事实证明这不是正确的(我现在不知道哪里错了)

正确的思路:

我们将数列分成三个部分一个是小于最开始中位数x(以下都用x来表示)的数字部分,x的部分,以及大于x的部分,设置小于的部分的长度是l,大于的部分的长度是r。我们从最终的情况来考虑,因为我们要让中位数发生变化,那么就只有两种情况,一个是变成了小于x,一个是变成大于x。而且我们必须使用最小的操作次数,那么分类讨论。变成小于的时候我们就不要删除任何小于x的数字,那这个时候新的中位数一定是第一个小于x的数字,可以想到最终的数列长度是2l。变大的情况类似于以上来讨论,新的中位数一定是第一个大于x的数字,假设最终的数列长度是偶数那么长度一定是2(r-1),如果是长度是奇数,那么长度一定是2(r-1)+1,对比长度之后发现是奇数的时候是更大的,也就说明需要删除的次数更小。

代码如下:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const ll mod=998244353;
int main(){
	int n;
	cin>>n;
	vector<int>a(n);
	for(int i=0;i<n;i++)cin>>a[i];
	sort(a.begin(),a.end());
	int mid=a[(n-1)/2];
	int l=lower_bound(a.begin(),a.end(),mid)-a.begin();
	int r=a.end()-upper_bound(a.begin(),a.end(),mid);
	if(l==0&&r==0){
		cout<<-1<<endl;
		return 0;
	}
	int ans=n+1;
	if(l>0)
		ans=min(ans,n-2*l);
	if(r>0)
		ans=min(ans,n-(2*r-1));
	cout<<ans<<endl;
}

总结:

有时候没有办法从一步一步来推导过程,可以像高中物理那样考虑初态和末态,从全局的角度来分析

3.Maximize The Sum

来源:https://ac.nowcoder.com/acm/contest/129231/E

开始的思路:

最开始我把三个数字的01组合都排列出来然后对他们进行异或操作(但是在比赛的时候我是忘了异或的操作是什么的,异或是两者相同为0,不同为1),我观察到这些操作有的会把1的个数变多有的会变少,首先我是没考虑进行导致1变少的操作的,然后有些操作会导致1的个数不变,那肯定是优先进行1的个数变多的操作,那就应该对每三个相邻的数中的1进行计数,把能变多的变多,变少的不变,而对于不变的我不知道怎么处理,因为这些操作会导致01的位置变化,所以可能会影响到下三个数字的情况;然后倒回来考虑将1变少的操作,如果这些操作改变了下一组的次序,却在总体上导致最后的1的总数变多的话,这种情况我也想象不到。所以我在列出八种操作的结果之后就没写这道题了。

正确的思路:

其实这道题在最终的解决上是很简单的,我没注意到那些操作结果导致1的个数不变的数字组有什么共同点,也没注意到这些操作导致的前后01次序的排列的变化,如果我注意到了的话比赛的时候说不定能写出来。好的规律是这样的,首先三个全是0的会导致操作之后还是全是0,三个里面一个1的会导致出现两个1(这里的次序的改变的规律没注意到也没事,因为后面的次序更重要),然后三个里面两个1的会导致1的个数不变,但是相对与原来的次序0的位置会被推到右边一个,最后三个里面全是1的会导致全部变成0,基于两个1的操作那么我们肯定可以把这里的0往右边推,只要这个字符串不全是0,那么推到右边的0一定会和1相邻这个时候如果三个里面只有1个1就可以多出来一个1,如果两个1,那就继续推,只要这样一直做下去我们肯定可以把左边全部变成1,最右边剩下一个0,那么答案就是n-1,但是两个特殊的情况,一个是已经说了的,就是全是0,那么答案肯定是0.如果最开始全是1,肯定是已经达到最大的数字了,就不需要操作。

总结:

最后用AI对我这道题的思路的解析我应该记牢异或和其他的位运算的操作结果,然后在做题的时候不要卡死在贪心的思路上,这种贪心不了的题需要从最终的全局来思考,比如说这道题可以思考我能否通过操作让字符串全部变成1?然后就是我不要光靠脑子想,可以多写一点数据来试一下

代码:

点击查看代码
	int t;
	cin>>t;
	while(t--){
		int n;
		string s;
		cin>>n>>s;
		int sum=0;
		for(int i=0;i<s.size();i++){
			if(s[i]=='1')
				sum++;
		}
		if(sum==n)
			cout<<sum<<endl;
		else if(sum==0)
			cout<<0<<endl;
		else 
			cout<<n-1<<endl;
	}
posted on 2026-03-28 14:56  wt_pav  阅读(2)  评论(0)    收藏  举报