D. Secret Santa(构造)

这是一个关于Codeforces比赛问题1530D的解决方案。题目涉及在一个'秘密圣诞老人'活动中,寻找一个最大化员工能够送给他们期望的人的礼物的分配策略。算法工程师通过构建并优化分配,确保尽可能多的员工能实现他们的愿望,同时避免自我分配。最终,给出了满足条件的最优分配方案。
摘要由CSDN通过智能技术生成

Problem - 1530D - Codeforces

 

每年12月,VK都会为其员工举办名为 "秘密圣诞老人 "的传统活动。它是这样发生的。

从1到n的n名员工参加了这个活动。每个员工i被分配到一个不同的员工bi,员工i必须给这个员工做一份新年礼物。每个员工正好被分配给另外一个员工,没有人被分配给自己(但两个员工可能被分配给对方)。从形式上看,所有bi必须是1到n之间的不同整数,对于任何i,bi≠i必须成立。

该分配通常是随机产生的。今年,作为一个实验,所有活动参与者都被问及他们希望向谁赠送礼物。每个员工i都说他们希望给员工ai送礼。

找到一个有效的分配b,使员工的愿望得到满足的数量最大化。

输入
每个测试包含多个测试案例。第一行包含测试用例的数量t(1≤t≤105)。测试用例的描述如下。

每个测试用例由两行组成。第一行包含一个整数n(2≤n≤2⋅105)--事件参与者的数量。

第二行包含n个整数a1,a2,...,an (1≤ai≤n; ai≠i) --雇员的愿望,从1到n依次排列。

保证所有测试案例的n之和不超过2⋅105。

输出
对于每个测试案例,打印两行。

在第一行,打印一个整数k(0≤k≤n)--在你的任务中实现的愿望的数量。

在第二行,打印n个不同的整数b1,b2,...,bn(1≤bi≤n;bi≠i)--分配给雇员1,2,...,n的雇员数。

k必须等于i的值的数量,使ai=bi,而且必须尽可能大。如果有多个答案,打印任何一个。

例子
输入复制
2
3
2 1 2
7
6 4 6 2 4 5 6
输出拷贝
2
3 1 2
4
6 4 7 2 3 5 1
注意
在第一个测试案例中,存在两个有效的赋值。[3,1,2]和[2,3,1]。前者满足了两个愿望,而后者只满足了一个。因此,k=2,唯一正确的答案是[3,1,2]。

题解:

根据题意,我们尽量让多的人可以送给想要的人

对于没有送礼物的,送给没有收到礼物的人

这样下去可能会有,自己送给自己的情况,我们记录下来,并且找到之前一样想送给相同人的人

交换他们俩,想送的情况,最优情况并不会改变,如上图 

#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<cstring>
#include<cmath>
using namespace std;
#define int long long
int a[200050];
int b[200050];
int d[200050];
int c[200050];
int e[200050];
void solve()
{
	int n;
	cin >> n;
	for(int i = 1;i <= n;i++)
	{
		c[i] = b[i] = 0;
	}
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
		c[a[i]] = 1;
	}
	int s = 0;
	for(int i = 1;i <= n;i++)
	{
		if(c[i])
		s++;
	}
	cout << s<<'\n';
	for(int i = 1;i <= n;i++)
	{
		if(c[a[i]])
		{
			c[a[i]] = 0;
			d[i] = a[i];
			b[a[i]] = 1; 
		}
		else
		{
			d[i] = 0;
		}
	}
	for(int i = 1,j = 1;i <= n;i++)
	{
		if(!d[i])
		{
			while(b[j])
			{
				j++;
			}
			d[i] = j++;
		}
	}
	for(int i = 1;i <= n;i++)
	e[d[i]] = i;
	for(int i = 1;i <= n;i++)
	{
		if(d[i] == i)
		{
			swap(d[i],d[e[a[i]]]);
		}
	}
	for(int i = 1;i <= n;i++)
	cout <<d[i]<<" ";
	cout <<"\n";
}

signed main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
//	cout.tie(0);
	int t = 1;
	cin >> t;
    while(t--)
	{
		solve();
	} 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值