Codeforces Round #658 (Div. 2)E Mastermind(补题)

题意:两个数组,长度一样。他们中有x个是一模一样,y个经过换序可以一样。给出长度n和x,y,再给出其中一个数组,问能不能有另一个数组符合要求。有就输出YES并打印这个数组,否则输出NO。

分析:以后我不做2500的题了,对自己太自信了。没有金刚钻,不去试这些玩意了。

首先对数组按数值从小到大排序,然后计算相同量,每个数和前一个数相同,相同量就是前一个数+1.然后按照相同量从大到小排序,把x的指标优先配发给相同量高的数。然后从没配发的(假设长度为len)开始按数值从小到大排序,配发值为a[i+len/2].val注意如果不分的话要加上已配发的数num再减去n(相当于没有配发的数绕成环)

令tmp=n-y;然后一一甄别这些数,要是配发的值和自己数值相等了,那就让他为额外的数(根据定义额外的数一定存在)tmp--。当tmp仍然存在的时候就选一些不被额外数替代的数变成这些值就可以了。

判no的条件是n-y+n-num<2*a[i].sam,因为这个情况下就算你把所有的值全部相等加上额外数,也抵消不掉相同的这么多。

一开始我想的是能不能先去x再去n-p,wa了不知道多少次。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
struct shu
{
	int val;//代表值
	int sam;//代表相同量
	int pos;//代表位置
	int cor;//代表对应值应该是多少 
}a[N];
int ji[N];//记录有哪些数已经被用到了
int unuse[N];//还有这些数没有被用过 
int cmp1(shu x1,shu x2)
{
	return x1.val<x2.val;
}
int cmp2(shu x1,shu x2)
{
	return x1.sam>x2.sam;
}
int cmp3(shu x1,shu x2)
{
	return x1.pos<x2.pos;
}
int n,x,y;
void print()
{
	sort(a,a+n,cmp3);
	printf("YES\n");
	for (int i=0;i<n;i++)
	{
		printf("%d ",a[i].cor);
	} 
	printf("\n");
}
void rlmn()
{
	scanf("%d%d%d",&n,&x,&y);
	for (int i=0;i<=n+1;i++)
	{
		ji[i]=0;
	}
	for (int i=0;i<n;i++)
	{
		scanf("%d",&a[i].val);
		a[i].pos=i;
		a[i].sam=1;//代表初始的时候只和自己相同 
		a[i].cor=-1;
		ji[a[i].val]=1;
	}
	//计算哪些颜色还没有被用过
	int tmp=0;
	for (int i=1;i<=n+1;i++)
	{
		if (ji[i]==0)
		{
			unuse[tmp++]=i;
		}
	} 
	//第一次排序,按大小排序,从而确定same数量 
	sort(a,a+n,cmp1);
	for (int i=1;i<n;i++)
	{
		if (a[i].val==a[i-1].val)
		{
			a[i].sam=a[i-1].sam+1;
		}
	}
	//第二次排序,按sam排序
	sort(a,a+n,cmp2);
	//用相同的指标x和额外数字的指标n-y来处理sam较大的一些数
	tmp=n-y;int num;int len;
	int flag=0;
	for (int i=0;i<n;i++)
	{
		if (x>0)
		{
			a[i].cor=a[i].val;
			x--;
			continue;
		}
		if (n-i+n-y<2*a[i].sam) 
		{
			printf("NO\n");
			return;
		}
		num=i;len=n-i;break;//两个指标都用完了,代表着还有num个数需要周旋。 
	}

	//第三次排序 ,按大小排序,这样就可以优先更换same大的 
	sort(a+num,a+n,cmp1);
	for (int i=num;i<n;i++)
	{
		if (i+len/2<n)
			a[i].cor=a[(i+len/2)].val;
		else
			a[i].cor=a[(i+len/2+num-n)].val;
	}
	tmp=n-y;
	for (int i=num;i<n;i++)
	{
		if (a[i].cor==a[i].val)
		{
			a[i].cor=unuse[0];
			tmp--;
		}
	}
	for (int i=num;i<n;i++)
	{
		if (tmp>0&&a[i].cor!=unuse[0])
		{
			a[i].cor=unuse[0];
			tmp--;
		}
	}
	//最后一次排序,按地址排回来
	print();
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)rlmn();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值