题意:给一个串以及串中每个字符可以交换的字符是谁,然后一次只可以交换两个字符, 可以交换零次或者多次。求合理的字典序最小的字符串
input:T -〉有多少组数据,初始串str, 接下来n(n = |str|)行,第i行表示第str中第i个字符可以替换成哪几个字符,
output: 如果没有一种合理的解决方法 : NO SOLUTION ,否则输出最小串
思路:二分图匹配, 每一个字符都会给你一个可以交换的序列,想要合法,那就必须使得每一个字符都有可交换的选择(本身也算一种交换)。我们可以字母当做一个节点(编号按字典序顺序,以便求出答案),如果可以置换,建边(单向) ,那么首先我们要检验的就是是否存在一个合理的交换方法,既二分图匹配(详情见代码)。 如果有,那么我们就要求字典序最小的, dfs枚举每一个字符替换成哪一个,然后检查剩下的还能不能成功匹配就好了,第一个成功的置换方法就是答案(因为我们是按顺序dfs)。
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
#define MAXN 105
bool g[MAXN][MAXN];
char str[MAXN], s[MAXN];
int match[MAXN], vis[MAXN], f[MAXN], ch[MAXN], cnt;
void init( )
{
memset(match, -1, sizeof(match));
memset(vis, 0, sizeof(vis));
memset(f, 0, sizeof(f));
memset(ch, 0, sizeof(ch));
}
int dfs( int u, int n)
{
for( int i = 1; i <= n; i++)
if(!f[i] && !vis[i] && g[u][i])//f->表示这个节点被没被选中过
{
vis[i] = 1;
if(match[i] == -1 || dfs(match[i], n))
{
match[i] = u;
return 1;
}
}
return 0;
}
int isok(int n)
{
int i, ans = 0;
memset(match, -1, sizeof(match));
for( i = 1; i <= n; i++)
{
if(ch[i]) continue;
memset(vis, 0, sizeof(vis));
if(dfs(i, n))
ans++;
}
return ans == cnt;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
memset(g, false, sizeof(g));
scanf("%s",str +1);
int len = strlen(str + 1);
sort(str + 1, str + len + 1);
for( int i = 1; i <= len; i++)
{
scanf("%s",s);
for( int j = 0; s[j]; j++)
for( int k = 1; k <= len; k++)
{
if(str[k] == s[j])
g[i][k] = 1;
}
}
int ans = 0;
for(int i = 1; i <= len; i++)
{
memset(vis, 0, sizeof(vis));
if(dfs(i, len)) ans++;
}
if(ans < len)
{
printf("NO SOLUTION\n");
continue;
}
memset(match, -1, sizeof(match));
cnt = len;
for( int i = 1; i <= len ;i ++)
{
for( int j = 1; j <= len; j++)
{
if(!f[j] && g[i][j])
{
ch[i] = j;
f[j] = 1;
cnt--;
if(isok(len))
break;
cnt++;
f[j] = 0;
}
}
}
for( int i = 1; i <= len; i++)
printf("%c",str[ch[i]]);
puts("");
}
return 0;
}