http://acm.hdu.edu.cn/showproblem.php?pid=1281
每行每列最多放置一个车,所以可以把行号和列号当成点,给定的点当成边进行最大匹配,得到的答案就是最大放置数了
然后,还要求重要点——转化为删除边后得到的最大匹配数是否发生变化的问题
易知重要点一定是模型中产生最大匹配时的边,所以我们枚举这样的边,尝试着删除判断情况
较优的做法是删边时将两个标号标记为未匹配,然后禁用这条边,在原有的基础上进行增广,若没有找到新的增广路,则重要点数+1,把两个标记复原
若找到了新的增广路,则我们又得到了一个最大匹配,这里就没有将两个标号复原了
之后将边的禁用取消,枚举下一条边
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=208; struct fuck{ int v,next; }edge[maxn*maxn]; int tol; int head[maxn]; void init() { tol=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v) { edge[tol].v=v; edge[tol].next=head[u]; head[u]=tol++; } int pre[maxn]; int bian[maxn]; bool vis[maxn]; bool ban[maxn*maxn]; bool used[maxn]; bool find(int u) { int i,v; for(i=head[u];i!=-1;i=edge[i].next) { v=edge[i].v; if(vis[v]||ban[i]) continue; vis[v]=true; if(pre[v]==-1||find(pre[v])) { pre[v]=u; bian[v]=i; return true; } } return false; } int hungary(int n) { int i,ans=0; memset(pre,-1,sizeof(pre)); for(i=1;i<=n;i++) { memset(vis,false,sizeof(vis)); if(find(i)) ans++; } return ans; } bool ck(int n) { int u; for(u=1;u<=n;u++) { if(used[u]) continue; memset(vis,false,sizeof(vis)); if(find(u)) { used[u]=true; return true; } } return false; } int main() { int i,j,n,m,k,u,v; int cas=1; while(scanf("%d%d%d",&n,&m,&k)==3) { init(); for(i=1;i<=k;i++) { scanf("%d%d",&u,&v); addedge(u,v+n); } memset(ban,false,sizeof(ban)); int ans=hungary(n); int sum=0; memset(used,false,sizeof(used)); for(j=n+1;j<=n+m;j++) if(pre[j]!=-1) used[pre[j]]=true; for(i=n+1;i<=n+m;i++) { if(pre[i]==-1) continue; int bitch=bian[i]; int shit=pre[i]; ban[bian[i]]=true; pre[i]=-1; used[shit]=false; if(!ck(n)) { sum++; pre[i]=shit; used[shit]=true; } ban[bitch]=false; } printf("Board %d have %d important blanks for %d chessmen. ",cas++,sum,ans); } return 0; }