首先,我们要清楚,这道题它是最小边覆盖的题。但是与其他不同的是,这道题要输出路径,所以说我们又要学习如何输出二分图路径:首先,先跑一次匈牙利,然后再从残余网络从X集出发,沿着增广路线依次走,标记途径节点,最后取X未标记的和Y已标记的输出答案
#include<iostream>
#include<cstdio>#include<algorithm>
#include<cstring>
using namespace std;
int vis[1005];
int visx[1005],visy[1005];
int linkx[1005],linky[1005];
int E[1005][2];
int a[1005][1005];
int n,m,k;
int x,y;
int find(int u){//先跑一遍匈牙利
for(int i=1;i<=n;i++){
if(vis[i]==0&&a[u][i]){
vis[i]=1;
if(linky[i]==0||find(linky[i])){
linkx[u]=i;linky[i]=u;
return 1;
}
}
}
return 0;
}
int buildtree(int u){//然后再跑一遍
visx[u]=1;
for(int i=1;i<=n;i++){
if(visy[i]==0&&a[u][i]){
visy[i]=1;
if(linky[i]==0||buildtree(linky[i]))return 1;
}
}
return 0;
}
void solve(){
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
ans+=find(i);
}
for(int i=1;i<=n;i++){//如果这一行有炸弹,且没被访问过
if(linkx[i]==0&&E[i][0])buildtree(i);
}
printf("%d",ans);
for(int i=1;i<=n;i++){
if(visx[i]==0&&E[i][0]){//如果没有被访问过,
//说明它这一行本来就是炸弹,因为它本来就是主动的那一方
printf(" r%d", i);
}
}
for(int i=1;i<=m;i++){
if(visy[i]&&E[i][1]) {//因为它是被动的那一方,所以
//如果它被访问过,说明它这一列就要放炸弹
printf(" c%d", i);
}
}
printf(" ");
}
void End(){
memset(a,0,sizeof(a));
memset(E,0,sizeof(E));
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
memset(linkx,0,sizeof(linkx));
memset(linky,0,sizeof(linky));
}
int main(){
while(scanf("%d%d%d",&n,&m,&k)!=EOF){
if(n==0&&m==0&&k==0)break;
for(int i=1;i<=k;i++){
scanf("%d%d",&x,&y);
a[x][y]=1;//二分图连边
E[x][0]=E[y][1]=1;//标记,说明这一行/列有炸弹
}
solve();
End();
}
return 0;
}