小希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的“车”,并且使得他们不能互相攻击,这当然很简单,但是Gardon限制了只有某些格子才可以放,小希还是很轻松的解决了这个问题(见下图)注意不能放车的地方不影响车的互相攻击。
所以现在Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。Gardon想让小希算出有多少个这样的重要点,你能解决这个问题么?
题解:和前面写过的一个二分匹配题类似,同样通过相当于对x,y缩点,这里缩点后其实就是行编号和列编号, 若有一个点可以放棋子,那么在这个点对应的行编号和列编号之间连边,因此产生一个二分图,所求答案就是这个二分图最大匹配。(匈牙利法) 最主要的是把这个问题想成二分图很神奇,并不是把一个格子想成一点,而是把行标和列标想成一点。 此题还需要求重要点,那么可以对二分图中每一条边(其实就是所给的能放棋子的点)删掉再求最大匹配, 如果小于先前所求最大匹配,那么就是重要点。
#include<iostream> #include<string.h> #include<vector> #define N 250 using namespace std; int n,m,k,map[N][N],x[N],y[N],mat[N]; bool vis[N]; bool find(int x) { for (int i=1;i<=n+m;i++){ if (map[x][i]){ if (vis[i])continue; vis[i]=true; if (!mat[i]||find(mat[i])){ mat[i]=x,mat[x]=i; return true; } } } return false; } int hungarian() { int sum=0,i; memset(mat,0,sizeof(mat)); for (i=1;i<=n+m;i++) if (!mat[i]){ memset(vis,false,sizeof(vis)); if (find(i))sum++; } return sum; } int main() { int id=0,i; while (~scanf("%d%d%d",&n,&m,&k)){ id++; memset(map,0,sizeof(map)); for (i=1;i<=k;i++){cin>>x[i]>>y[i];y[i]+=n;map[x[i]][y[i]]=map[y[i]][x[i]]=1;} int Max=hungarian(),ans; for (i=1,ans=0;i<=k;i++){ map[x[i]][y[i]]=0; map[y[i]][x[i]]=0; int res=hungarian(); if (res<Max)ans++; map[x[i]][y[i]]=map[y[i]][x[i]]=1; } printf("Board %d have %d important blanks for %d chessmen.\n",id,ans,Max); } return 0; }
AC!
原文:https://www.cnblogs.com/live-no-regrets/p/14053955.html