这题值得一做,,,路径的打印值得一学。
——>>以每行为X结点,每列为Y结点,有石头的点的横纵坐标关系为边建立二分图,那么,对于一个石头的位置,就是一条边,只要在这条边2个端点中的1个设大炮就可打掉这个石头,这正正是二分图的最小点覆盖。
对于具体的位置,则可从X中所有的未盖点出发,寻找增广路,并且标记沿途经过的结点,那么X中未被标记的点和Y中被标记的点即为所求,这是因为:
从X中的未盖点出发,终点一定在X结点(否则存在增广路,与最大匹配矛盾),那么这次增广中标记Y结点的数量就会比标记X结点的数量少1,故取已标记的Y结点用的大炮就会比取已标记的X结点的数量要少1。
对于X中未被标记的结点a,首先a是已盖点,Y中必有结点b与之匹配,且b一定不和X中的未盖点相连(否则a,b一定被标记了),若a还与Y中的未盖点相连,则应取a,而不是b及与a相连的Y中的未盖点,这样只在a一个点设大炮就可解决掉这几个石头;若a连的还有Y中的已盖点,那么那点由它的匹配点去处理,a不用去处理它;若a不与Y的其它点相连,那么取a点或者取b都行,我取了a点,所以这些情况取已盖点a就是。
综上可得取X中未被标记的点和Y中被标记的点就是设大炮的行与列。
// #pragma comment(linker, "/STACK:1024000000,1024000000") /* 这题是最小点覆盖问题,然后确定炮弹放的位置。显然先跑遍匈牙利, 然后确定位置。跑完匈牙利后,对于左右两个集合而言,如果x集合中 的点x[i]没有匹配,就是linkx[i] == -1,那么有两种情况,第一 种是根本就不存在y集合中的点与其连接(此事就不用放),第二种是 与其连接的y集合中的点已经被其他x中的点匹配了(此事就放在y中的 这个点上面)。也就是此事就是一点对多点,现在会选择这个点; */ #include <iostream> #include <algorithm> #include <iomanip> #include <sstream> #include <string> #include <stack> #include <queue> #include <deque> #include <vector> #include <map> #include <set> #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> #include <limits.h> // #define DEBUG #ifdef DEBUG #define debug(...) printf( __VA_ARGS__ ) #else #define debug(...) #endif #define MEM(x,y) memset(x, y,sizeof x) using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> ii; const int inf = 1 << 30; const int INF = 0x3f3f3f3f; const int MOD = 1e9 + 7; const int maxn = 1010; vector<int> G[maxn]; int linky[maxn], linkx[maxn]; int r,c,n; int cnt; bool usedy[maxn], usedx[maxn]; bool dfs(int u){ usedx[u] = true; for (int i = 0;i < G[u].size();++i){ int v = G[u][i]; if (usedy[v]) continue; usedy[v] = true; if (linky[v] == -1 || dfs(linky[v])){ linky[v] = u; linkx[u] = v; return true; } } return false; } void solve(){ MEM(linky, -1); MEM(linkx, -1); for (int i = 0;i <= r;i++){ if (G[i].size()){ MEM(usedy, false); if (dfs(i)) cnt++; } } printf("%d",cnt);//需要的炮弹数目 //确定放的位置 MEM(usedy, false); MEM(usedx, false); /*这里就是checkx集合中的点。如果这个点被匹配了,先不管 然后就是check未匹配的点,不管有无其他点与其连接,这里都 显然不用放炮弹。*/ for (int i = 1;i <= r;i++) if (linkx[i] == -1) dfs(i); for (int i = 1;i <= r;i++) if (!usedx[i]) printf(" r%d",i); for (int i = 1;i <= c;++i) if (usedy[i]) printf(" c%d",i); puts(""); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); while(scanf("%d%d%d",&r,&c,&n) && (r+c+n)){ //Init(); for(int i=0;i<=r;i++) G[i].clear(); cnt = 0; int a,b; while(n--){ scanf("%d%d",&a,&b); G[a].push_back(b); } solve(); } return 0; }