codeforces 1382E. Mastermind

题意:n个数的数组a,b,每个数∈[1,n+1],已知数组b,构造数组a使a,b刚好有x个数在相同位置匹配,刚好有y个数相同(不考虑位置)

按照使最多的数的个数最少的原则用priority_queue分配前x个数,剩下n-x个数中有n-y个数与b中相等但位置不同,最后剩余的数用没出现过的数填充. 主要问题即n-x数中选出y-x数个不匹配,若n-x中出现最多的数的数量f不超过n-x的一半,显然可以将每个数右移[n-x/2]的单位达到,若超过一半,则此时最多不匹配数为2*(f-n+x),若y-x大于这个值,则不成立

#include <bits/stdc++.h>

using namespace std;

#ifdef LOCAL
    #define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
    #define eprintf(...) 42
#endif

using ll = long long;
using ld = long double;
using D = double;
using uint = unsigned int;
template<typename T>
using pair2 = pair<T, T>;
using pii = pair<int, int>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second

const int maxn = 100005;

vector<int> pos[maxn];
int n, bulls, cows;
priority_queue<pair2<int>> freq;
int a[maxn], b[maxn];
vector<pair2<int>> all;
void solve()
{
	scanf("%d%d%d", &n, &bulls, &cows);
	cows -= bulls;
	for (int i =0 ; i < n + 1; i++) pos[i].clear();
	for (int i =0 ; i < n; i++)
	{
		scanf("%d", &b[i]);
		b[i]--;
		pos[b[i]].pb(i);
	}
	for (int i = 0; i < n; i++) a[i] = -1;
	int missing_color = -1;
	while (!freq.empty()) freq.pop();
	for (int i = 0; i < n + 1; i++)
	{
		if (pos[i].empty()) missing_color = i;
		freq.push({(int)pos[i].size(), i});
	}
	assert(missing_color != -1);
	for (int i =0 ; i < bulls; i++)
	{
		int color = freq.top().se;
		freq.pop();
		a[pos[color].back()] = color;
		pos[color].pop_back();
		freq.push({(int)pos[color].size(), color});
	}
	all.clear();
	int max_occ = freq.top().fi;
	while (!freq.empty())
	{
		int color = freq.top().se;
		freq.pop();
		//cout << color << ' ' << (int)pos[color].size() << endl;
		for (auto t : pos[color]) all.pb({color, t});
	}
	//cout << "f " << (int)all.size() << ' ' << max_occ << endl;
	int max_cows = (int)all.size();

	if (2 * max_occ > (int)all.size()) max_cows -= 2 * max_occ - (int)all.size();
    //如果最大的数f出现超过总数sum一半,则可以找到的最大两两不同的数字对大小为2*(sum - f)(参考Gym - 101291I, https://blog.csdn.net/qq_43305984/article/details/88749031)
	if (cows > max_cows)
	{
		printf("NO\n");
		return;
	}
	int shift = (int)all.size() / 2;
    //若每个数<=shift/2个,显然每次向右移shift个格子成立, 若该数向右shift格子仍然相等,则把该数设为没出现过的数 
    // 6 1 6
    // 1 2 3 4 5 6
    // Yes
    // 4 5 1 2 3 6
	for (int i = 0; i < (int)all.size(); i++)
	{
		int j = (i + shift) % (int)all.size();
		int color = all[j].fi;
		if (all[i].fi == all[j].fi || cows == 0) color = missing_color;
		if (color != missing_color) cows--;
		a[all[i].se] = color;
	}
	printf("Yes\n");
	for (int i = 0; i < n; i++) printf("%d ", a[i] + 1);
	printf("\n");
}

int main()
{
	int NT;
	scanf("%d", &NT);
	for (int T = 0; T < NT; T++)
	{
		solve();
	}
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值