题目:SAM I AM(我是后缀自动机)
思路:
二分图最小点覆盖问题。
即把行和列看做点,点看做边,跑最小点覆盖(二分图最大匹配)。
输出路径有点麻烦。
根据匈牙利算法的性质可以知道,每个没有被匹配的端点都至少和一个已经匹配了的点相连。
所以可以想到,只需要从没有匹配的横线开始找,把和它相连的纵线标记即可,然后这个被标记的纵线连接的横线就取消标记。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000
#define read(x) scanf("%d",&x)
int n,m,k;
vector<int> g[maxn+5];
int mtch[maxn+5];
bool pl[maxn+5],pr[maxn+5];
bool v[maxn+5];
bool dfs(int x) {
pl[x]=1;
for(int i=0;i<g[x].size();i++) {
int y=g[x][i];
if(!pr[y]) pr[y]=true;
else continue;
if((!mtch[y])||dfs(mtch[y])) {
mtch[y]=x;
return true;
}
}
return false;
}
int main() {
while((~read(n))&&n) {
for(int i=1;i<=maxn;i++) g[i].clear();
memset(mtch,0,sizeof(mtch));
memset(v,0,sizeof(v));
read(m),read(k);
for(int i=1;i<=k;i++) {
int x,y;
read(x),read(y);
g[x].push_back(y);
}
int sum=0;
for(int i=1;i<=n;i++) {
memset(pr,0,sizeof(pr));
if(dfs(i)) sum++;
}
printf("%d",sum);
memset(pl,0,sizeof(pl)),memset(pr,0,sizeof(pr));
for(int i=1;i<=m;i++) v[mtch[i]]=true;
for(int i=1;i<=n;i++) {
if(!v[i]) dfs(i);
}
for(int i=1;i<=n;i++) if(!pl[i]) printf(" r%d",i);
for(int i=1;i<=m;i++) if(pr[i]) printf(" c%d",i);
printf("\n");
}
return 0;
}