题目
m(m<=1e5)个人,每个人有一个长度为3的仅由w、i、n组成的串,
且m个人恰有m个w,m个i,m个n,
现在需要交换,每次可以选择两个人a、b,将a的某一个字母和b的某一个字母交换
若干次交换后,使得每个人手中的串恰好是win
输出最小的交换次数,以及对应的交换序列
实际t(t<=1e4)组数据,保证m总和不超过1e5
思路来源
cuiaoxiang代码
题解
赛中搞了150行,用了1个小时,实际60-70行,20分钟就差不多了
首先,把win字母对应标记成012,则只有10种情况,其中012是不用换的
000,111,222
001,110
002,220
112,221
012
而,0多1少,对应000和002两种情况,记到vector<int>a[0][1]内,表示0需要给1一个
其他对应情况,依次类推
其中,000,既出现在0多1少的情况中,也出现在0多2少的情况中
1. 二元环,a[0][1]<->a[1][0],a[0][2]<->a[2][0],a[1][2]<->a[2][1],优先操作
2. 不能操作后,则只剩三元环,a[0][1]->a[1][2]->a[2][0],a[1][0]->a[0][2]->a[2][1],操作即可
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<array>
using namespace std;
const string S="win";
int T,n;
vector<int>a[3][3];
int g(char c){return S.find(c);}
bool has(vector<int>&x){return !x.empty();}
int get(vector<int>&x){int y=x.back();x.pop_back();return y;}
string s;
int main(){
cin>>T;
while(T--){
cin>>n;
for(int i=0;i<3;++i){
for(int j=0;j<3;++j){
a[i][j].clear();
}
}
for(int i=1;i<=n;++i){
cin>>s;
vector<int>cnt(3);
for(int j=0;j<3;++j){
cnt[g(s[j])]++;
}
for(int x=0;x<3;++x){
for(int y=0;y<3;++y){
if(x==y)continue;
if(cnt[x]>1 && cnt[y]<1){
a[x][y].push_back(i);
cnt[x]--;
cnt[y]++;
}
}
}
}
vector<array<int,4>>ans;
for(int x=0;x<3;++x){
for(int y=x+1;y<3;++y){
vector<int>&fi=a[x][y];
vector<int>&se=a[y][x];
while(has(fi) && has(se)){
int f=get(fi),s=get(se);
ans.push_back({f,x,s,y});
}
}
}
{
vector<int>&fi=a[1][0];
vector<int>&se=a[0][2];
vector<int>&th=a[2][1];
while(has(fi) && has(se) && has(th)){
int f=get(fi),s=get(se),t=get(th);
ans.push_back({f,1,s,0});
ans.push_back({s,1,t,2});
}
}
{
vector<int>&fi=a[0][1];
vector<int>&se=a[1][2];
vector<int>&th=a[2][0];
while(has(fi) && has(se) && has(th)){
int f=get(fi),s=get(se),t=get(th);
ans.push_back({f,0,s,1});
ans.push_back({s,0,t,2});
}
}
cout<<(int)ans.size()<<endl;
for(auto [i,x,j,y]:ans){
cout<<i<<" "<<S[x]<<" "<<j<<" "<<S[y]<<'\n';
}
}
return 0;
}