暑假-二分图-D - Uncle Tom's Inherited Land

来源:互联网 发布:上海知恩服饰有限公司 编辑:程序博客网 时间:2024/05/01 16:38

题意:有个n*m的地,有k个位置有水塘,现在想把这些地卖了【卖的地必须是1*2的长方形,并且长方形不含水塘】

问最多能卖多少块地。

思路:二分图的最大边独立集,等于最大匹配。

对于一个位置mapg[i][j]如果此位置不是水塘,则判断此位置上下左右四个方向的位置是否为水塘,若不是水塘则

说明可以与此位置的空地组成一块能卖的长方形【即此位置也上下左右不是水塘的位置连一条边】。最后走一遍

二分图最大匹配模板就可。

注意:由于边的两个点是相互的, 即1能和2组成长方形,那么2也能和1组成长方形,所以最终求得的结果

为正真结果的两倍。


/*628KB0MS*/#include<iostream>#include<cstring>#include<vector>#include<stdio.h>using namespace std;const int MAXN = 105;const int MAXP = 10005;//n,m最大为100,如果没有水塘那么点最多是100*100#define INF 65535//一个不可能达到的值,标记为水塘int mapg[MAXN][MAXN];//初始图int link[MAXP];//点i也二分图另一半的哪个顶点匹配int vis[MAXP];//判断是否访问过int dist[4][2] = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } };vector <int> map[MAXP];//邻接表存储二分图【主图】int n, m, k;bool find(int x){for (int i = 0; i < map[x].size(); i++)//考虑所以和x有关系的点{int u = map[x][i];if (!vis[u])//x与u邻接,且没有访问过{vis[u] = 1;//标记访问if (link[u] == -1 || find(link[u])){//如果u没有匹配,或者u已经匹配了,但从link[u]出发可以找到增广路link[u] = x;//把x匹配给ureturn true;}}}return false;}void init()//初始化{memset(link, -1, sizeof(link));memset(mapg, 0, sizeof(mapg));}int main(){while (scanf("%d%d",&n,&m)!=EOF){if (n == 0 && m == 0){break;}init();//初始化int u, v, ans = 0, p = 0;scanf("%d", &k);while (k--){scanf("%d%d", &u, &v);mapg[u][v] = INF;//水塘位置标记}for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){if (mapg[i][j]!=INF)//把不是水塘的位置给一个编码{mapg[i][j] = p++;//从0到((n*m)-k-1)}}}for (int i = 1; i <= n; i++){for (int j = 1; j <= m; j++){if (mapg[i][j] != INF){for (int v = 0; v < 4; v++)//上下左右四个方向。{int x = i + dist[v][0];int y = j + dist[v][1];if ((x > 0 && x <= n) && (y > 0 && y <= m) && mapg[x][y] != INF){//不越界并且该位置不是水塘map[mapg[i][j]].push_back(mapg[x][y]);//则连一条边}}}}}for (int i = 0; i < p ; i++){memset(vis, 0, sizeof(vis));if (find(i)) //从每个未盖点出发进行寻找增广路{//每找到一条增广路,可使得匹配数加1ans++;}}printf("%d\n",(ans/2));for (int i = 0; i < p; i++)//清除数组{map[i].clear();}}return 0;}


0 0