D - Swap Free Gym - 102423D 二分图性质:补图最大团 = 点的个数 - 最大匹配数
题意:给你一个串的某些全排列,没有重的,让你求一个最大的集合能有多少个元素,集合的满足条件:交换一个串的任意两个位置上的字母,不能变成集合里的另一个串。
思路:如果一个串不能通过交换一次字母位置变成另一个串,就让这两个串建边。建好图之后,找一个最大完全图,表示任意两个都不能通过交换变成相同的串。但是提交后发现找最大团的算法会TLE,就要用到题目的性质了。补图最大团 = 点的个数 - 最大匹配数,这样我们就让能交换到达的建边。
#include<bits/stdc++.h> #define pb push_back using namespace std; const int maxn = 505; vector<int> ma[maxn]; char MAP[550][30]; int link[maxn]; bool vis[maxn]; bool dfs(int x){ int len = ma[x].size() - 1; for(int i = 0; i <= len; i++){ int v = ma[x][i]; if(vis[v]) continue; vis[v] = 1; if(!link[v] || dfs(link[v])){ link[v] = x; link[x] = v; return 1; } } return 0; } int solve(int n){ int ans = 0; for(int i = 1; i <= n; i++){ if(link[i]) continue; for(int j = 1; j <= n; j++) vis[j] = 0; if(dfs(i)) ans++; } return n - ans; } int main(){ int n; scanf("%d",&n); getchar(); for(int i = 1;i <= n;i++){ gets(MAP[i]); } int len = strlen(MAP[1]); for(register int i = 1;i <= n;i++){ for(register int j = i + 1;j <= n;j++){ int sum = 0; for(register int k = 0;k < len;k++){ if(MAP[i][k] != MAP[j][k]){ sum++; if(sum > 2) break; } } if(sum == 2) { ma[i].pb(j); ma[j].pb(i); } } } printf("%d\n",solve(n)); return 0; }