HDU 4185 Oil Skimming(匈牙利)
二分图最大匹配问题之匈牙利算法
一:匈牙利算法的原理:
从当前的匹配M(如果木有匹配,则取初始化匹配M为空集)出发,检查每一个未盖点,然后从它出发寻找可增广路,找到可增广路,则沿着这条可增广路进行扩充,直到不存在可增广路为止。
二:根据从未盖点出发寻找可增广路搜索的方法,可以分成:1.DFS增广 2.BFS增广 3.多增广(Hopcroft-Karp算法)
采用DFS思想搜索可增广路并求出最大匹配的代码如下:
#define MAX 101//MAX为表示X集合和Y集合顶点个数最大值的符号常量 int vis[MAX] ;//记录顶点访问状态的数组,为1时表示已经访问过,为0时表示未被访问过 int nx,ny; //表示集合X,Y集合中的个数 int map[MAX][MAX]; //邻接矩阵,为1时表示Xi 和Yj有边相连 int dx[MAX],dy[MAX];//dx[i]表示最终求得最大匹配中与Xi 匹配的Y顶点,dy[]同理 int DFS(int v)//这种增广使得当前匹配数增加1 { for(int i=0;i<ny;i++)//考虑到所有Yi 顶点i if(map[v][i]&&!vis[i])//v与i 相连并且没有访问过 { vis[i]=1;//访问i 标记为1 if(dy[i]==-1||DFS(dy[i]))//如果i 没有匹配,或者i 已经匹配了不过从dy[i]出发可以找到一条增广路 {//注意 如果i 没有匹配 那么后面的DFS(dy[i])这个递归过程不会运行 dx[v]=i;//把i 匹配给v dy[i]=v;//把v 匹配给i return 1; } } return 0;//表示不存在从v出发的增光路 } int Hungary() { int ans=0;//求得的最大匹配数 memset(dx,0xff,sizeof(dx));//初始化为-1 memset(dy,0xff,sizeof(dy));//初始化为-1 for(int i=0;i<=nx;i++)//for结束意味着寻找增光路结束,最大匹配诞生 if(dx[i]==-1)//如果是未盖点则出发进行寻找增广路 { memset(vis,0,sizeof(vis));//初始化 ans+=DFS(i);//没找到一条增广路则匹配数累加1 } return ans; }
Oil Skimming
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 311 Accepted Submission(s): 168
Problem Description
Thanks to a certain "green" resources company, there is a new profitable industry of oil skimming. There are large slicks of crude oil floating in the Gulf of Mexico just waiting to be scooped up by enterprising oil barons. One such oil baron has a special plane that can skim the surface of the water collecting oil on the water's surface. However, each scoop covers a 10m by 20m rectangle (going either east/west or north/south). It also requires that the rectangle be completely covered in oil, otherwise the product is contaminated by pure ocean water and thus unprofitable! Given a map of an oil slick, the oil baron would like you to compute the maximum number of scoops that may be extracted. The map is an NxN grid where each cell represents a 10m square of water, and each cell is marked as either being covered in oil or pure water.
Input
The input starts with an integer K (1 <= K <= 100) indicating the number of cases. Each case starts with an integer N (1 <= N <= 600) indicating the size of the square grid. Each of the following N lines contains N characters that represent the cells of a row in the grid. A character of '#' represents an oily cell, and a character of '.' represents a pure water cell.
Output
For each case, one line should be produced, formatted exactly as follows: "Case X: M" where X is the case number (starting from 1) and M is the maximum number of scoops of oil that may be extracted.
Sample Input
1
6
......
.##...
.##...
....#.
....##
......
Sample Output
Case 1: 3
Source
Recommend
lcy
题意:现在有大量的原油漂浮在大海上,每10*10面积的油用一个'#'表示,现用一把10*20的勺子去舀油,且舀出的油不能混合海水(即只能舀上下左右相邻的油),问最多能舀多少勺出来?
分析:其实就是问上下左右相邻的油最多能舀出多少对?用二分图匹配中的匈牙利算法做,油即代替为顶点,构图时再虚拟出一组相同的顶点,然后将两滴相邻的油进行匹配,算最大匹配时过程中会多算出1倍,最后得出的最大匹配再除2就是我们想要的最大舀数。
#include<iostream> using namespace std; #define MAX 610 int vis[MAX] ; int n,sum;//sum记录#的个数 int map[MAX][MAX],pos[MAX][MAX]; int dx[MAX],dy[MAX]; int DFS(int v) { for(int i=0;i<=sum;i++) if(map[v][i]&&!vis[i]) { vis[i]=1; if(dy[i]==-1||DFS(dy[i])) { dx[v]=i; dy[i]=v; return 1; } } return 0; } int Hungary() { int ans=0; memset(dx,-1,sizeof(dx)); memset(dy,-1,sizeof(dy)); for(int i=0;i<=sum;i++) if(dx[i]==-1) { memset(vis,0,sizeof(vis)); ans+=DFS(i); } return ans; } int main() { int t,i,j,cases=0; char a[MAX][MAX]; scanf("%d",&t); while(t--) { cases++; sum=0; memset(map,0,sizeof(map)); scanf("%d",&n); for(i=0;i<n;i++) { scanf("%*c"); for(j=0;j<n;j++) { scanf("%c",&a[i][j]); if(a[i][j]=='#') pos[i][j]=sum++; } } for(i=0;i<n;i++) for(j=0;j<n;j++) if(a[i][j]=='#') {//对上下左右相邻的油进行匹配 if(i-1>=0&&a[i-1][j]=='#') map[pos[i][j]][pos[i-1][j]]=1; if(i+1<n&&a[i+1][j]=='#') map[pos[i][j]][pos[i+1][j]]=1; if(j-1>=0&&a[i][j-1]=='#') map[pos[i][j]][pos[i][j-1]]=1; if(j+1<n&&a[i][j+1]=='#') map[pos[i][j]][pos[i][j+1]]=1; } printf("Case %d: %d\n",cases,Hungary()/2); } return 0; }