D. Swap Free / I. Error Correction
题意
给你n个长度相同,包含字母种类相同,每种字母数量相同,让你确定一个字符串集合,集合中的任意一个串不能通过交换任意两个不同的位置变成集合中的另一个串,
比如说 abc和acb就可以通过第二个字符串交换cb的位置就相等了,
问你集合最大有多个字符串。
思路:
如果A单词经过互换俩字母可以变成B单词,然后A单词经过互换俩字母也可以变成C单词;那么B单词一定不能经过字母互换变成C单词。
符合染色法,可以用二分图;
问集合最大有多少字符串,也就是求这个二分图的最大独立集。
二分图的基本知识
最大独立集:任意俩点之间都没有边相连的点集称为独立集,那么最大独立就是点数最多:
最大团:任意俩点之间都有一条边相连的子图称为无向图的团,点数最多的团称为最大团
最大独立集=最小边覆盖=n-最大匹配数
最大团=原图的补图的最大独立集
代码
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxx = 2e5 + 100;
int ma[505][505];
char s[505][30];
bool book[505];
int match[505];
int n, w;
bool find(int u) //二分图模板
{
for (int i = 1; i <= n; i++)
{
if (ma[u][i] && book[i] == 0)
{
book[i] = 1;
if (match[i] == 0 || find(match[i]))
{
match[i] = u;
return 1;
}
}
}
return 0;
}
int main()
{
scanf("%d%d", &n, &w);
for (int i = 1; i <= n; i++)
{
scanf("%s", s[i]);
}
//建图过程,可以经过一次转换变成令一个单词就建边
for (int i = 1; i <= n; i++)
{
int len1 = strlen(s[i]);
for (int j = i + 1; j <= n; j++)
{
int len2 = strlen(s[j]);
if (len1 != len2) //长度不符合
{
ma[i][j] = 0;
continue;
}
else //长度符合
{
int num = 0; //记录不同的个数
int x1 = -1; //记录第一个不同的位置
int x2 = -1; //记录第二个不同的位置
for (int k = 0; k < len1; k++)
{
if (s[i][k] != s[j][k])
{
num++;
if (num == 1)
{
x1 = i;
}
else if (num == 2)
{
x2 = i;
}
}
if (num >= 3)
break;
} //如果不同有俩个,并且这俩交换后是相等的,那么就建边
if (num == 2 && ma[i][x1] == ma[j][x2] && ma[i][x2] == ma[j][x1])
ma[i][j] = ma[j][i] = 1;
else
ma[i][j] = 0;
}
}
}
int s = 0;
for (int i = 1; i <= n; i++)
{ //跑二分图
memset(book, 0, sizeof(book));
if (find(i))
s++;
}
//因为是无向图,所以有除二,然后就最大独立集
printf("%d\n", n - s / 2);
return 0;
}