http://poj.org/problem?id=3020
题意:给你一个n*m的图,'*'表示城市,每个无线网只能覆盖两个城市,且这两个城市只能东南西北相邻,求最小用多少这样的无线网可以覆盖所有的城市。
ps:做完这道题我感觉英语不好这题就做不出来。。
思路:无向图的最小边覆盖。刚开始各种看不懂题我就不说了= =。一开始一看是矩阵以为和上一道题一样,结果感觉这才是真正的二分匹配变形。二分匹配还可以这么玩!把每个城市编号,无向图的最小边覆盖相当于最大匹配数+相对孤立节点数。不过顶点数减去这个最小边覆盖依然等于最大匹配数(匹配是两两成对的),所以还是求最小边覆盖嘛!(感觉绕了个弯。。)这个还加了点搜索的思想,加点条件就行,四个方向每发现一个点就建立当前点到目的点的单向边,这样边就不会算漏。因为是无向图,匹配数记得除二哈。好在数据不是很大,1A,哦也~
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>
#include <stack>
#include <ctype.h>
using namespace std;
typedef long long LL;
const int N = 505;
const int INF = 0x3f3f3f3f;
int head[N], match[N];
bool vis[N];
int cnt;
struct Edge
{
int to,next;
}edge[N*N];
void add(int u, int v)
{
edge[cnt] = (struct Edge){v, head[u]};
head[u] = cnt++;
}
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
bool Augment(int u)
{
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(!vis[v])
{
vis[v] = true;
if(match[v]==-1 || Augment(match[v]))
{
match[v] = u;
return true;
}
}
}
return false;
}
int Hungary(int num)
{
int ans = 0;
memset(match, -1, sizeof(match));
for(int i = 1; i <= num; i++)
{
memset(vis, false, sizeof(vis));
if(Augment(i)) ans++;
}
return ans;
}
int main()
{
// freopen("in.txt", "r", stdin);
int t, n, u, v, m;
int G[N][N];
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
init();
char x;
int num = 1;
memset(G, -1, sizeof(G));
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
cin >> x;
if(x == 'o') G[i][j] = 0;
else if(x == '*')
{
G[i][j] = num;
num++;
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
if(G[i-1][j] >= 1) add(G[i][j], G[i-1][j]);
if(G[i+1][j] >= 1) add(G[i][j], G[i+1][j]);
if(G[i][j-1] >= 1) add(G[i][j], G[i][j-1]);
if(G[i][j+1] >= 1) add(G[i][j], G[i][j+1]);
}
num--;
printf("%d\n", num-Hungary(num)/2);
}
return 0;
}