优盈娱乐-优盈娱乐注册「官网登陆」

优盈娱乐-优盈娱乐注册「官网登陆」专注于回合制战斗,并且围绕游戏开办了专业比赛,优盈娱乐-优盈娱乐注册「官网登陆」是一个以游戏交流、娱乐休闲为主的温馨家园.我们提供最新的游戏资讯,优盈娱乐-优盈娱乐注册「官网登陆」游戏等众多项目,共同打造全新公益模式。

大白叔叔专题之匹配、网络流(二)(第一题不

1、poj 2195 Going Home(二分图最小权匹配)

1、hdu 2444 The Accomodation of Students(判断二分图+最大匹配)(匈牙利模板)

  题意:图上有n个房子,n个人,现在安排每个人回到一所房间,求最小的步数和。

  题意:一共有n个学生,m对关系:A认识B。问能否将所有的人分成两批,每批之间的人都互相认识,如果可以,输出每批的人数。即判断是否为二分图,以及求二分图的最大匹配。

  思路:KM算法模板题。注意反向。

  思路:判断是否为二分图(DFS或BFS);求二分图的最大匹配:匈牙利算法。

  附:推荐km算法大神博客:

图片 1图片 2

    

 1 #include<iostream>
 2 #include<queue>
 3 using namespace std;
 4 int n,m;
 5 const int maxn = 210;//x集合和y集合总最大的点数
 6 bool mp[maxn][maxn];//1表示该ij可以匹配
 7 int cx[maxn];//记录x集合中匹配的y元素是哪一个
 8 int cy[maxn];//记录y集合中匹配的x元素是哪一个
 9 int vis[maxn];//标记该顶点是否访问过
10 int cntx;
11 bool dfs(int u)
12 {
13     for (int v = 1; v <= n; v++)//两个集合内共有n个元素
14     {
15         if (mp[u][v] && !vis[v])
16         {
17             vis[v] = 1;
18             if (cy[v] == -1 || dfs(cy[v]))//)//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路
19             {
20                 cx[u] = v; cy[v] = u;
21                 return 1;
22             }
23         }
24     }
25     return 0;
26 }
27 int maxmatch()//匈牙利算法主函数
28 {
29     int ans = 0;
30     memset(cx, 0xff, sizeof cx);//初始值为-1表示两个集合中都没有匹配的元素!
31     memset(cy, 0xff, sizeof cy);
32     for (int i = 1; i <= n; i++)
33         if (cx[i] == -1)//如果i未匹配
34         {
35             memset(vis, 0, sizeof(vis));
36             ans += dfs(i);
37         }
38     return ans/2;//对两个部里的都匹配了,这样就相当于匹配了两次了  
39 }
40 bool istwo()
41 {//判断是否为二分图
42     queue<int>q;
43     memset(vis, 0, sizeof(vis));
44     q.push(1);
45     vis[1] = true;
46     while (!q.empty())
47     {
48         int u = q.front();
49         q.pop();
50         for (int i = 1; i <= n; i++)
51         {
52             if (mp[u][i])
53             {
54                 if (vis[i] == 0)
55                 {
56                     if (vis[u] == 1) vis[i] = 2;
57                     else vis[i] = 1;
58                     q.push(i);
59                 }
60                 else
61                 {
62                     if (vis[i] == vis[u]) return false;
63                 }
64             }
65         }
66     }
67     return true;
68 }
69 int main()
70 {
71     while (~scanf("%d%d", &n, &m))
72     {
73         memset(mp ,0, sizeof(mp));
74         while (m--)
75         {
76             int a, b;
77             scanf("%d%d", &a, &b);
78             mp[a][b] = mp[b][a] = 1;
79         }
80         if (!istwo()|| n == 1)
81         {
82             printf("Non");
83         }
84         else
85         {
86             int ans = maxmatch();
87             printf("%dn", ans);
88         }
89     }
90 
91     return 0;
92 }

    

View Code

    

2、hdu 1083 Courses(最大匹配)

    

  题意:有P种课程,N个学生。接下来P行,第i行第一个Ni表示喜欢第i个课程的学生的人数,接下来是Ni个学生。问:能否有一种匹配使得每个学生都选一门不同的课程,同时所有课程都出现。

    

  思路:二分图最大匹配,匈牙利算法,课程为x集,学生为y集。判断最大匹配数是否为P。

图片 3图片 4

图片 5图片 6

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 using namespace std;
  5 #define N 105
  6 #define INF 0x3f3f3f
  7 char maze[N][N];
  8 int mp[N][N], match[N], lx[N], ly[N], visx[N], visy[N], slack[N];
  9 int n, m,ny,nx;
 10 //lx,ly为顶标,nx,ny分别为x点集y点集的个数
 11 //match数组记录右边y端点所连的左端点x,visx,visy数组记录是否曾访问过,也是判断是否在增广路上
 12 struct node
 13 {
 14     int a, b;
 15 }sa[N], sb[N];
 16 //KM求二分图最小匹配模板:只需把权值都变成负的,再用KM算出最大权匹配,然后取反就是答案
 17 //学习KM地址http://blog.sina.com.cn/s/blog_691ce2b701016reh.html
 18 bool dfs(int x)
 19 {
 20     visx[x] = 1;
 21     for (int y = 1; y <= ny; y++)
 22     {
 23         if (visy[y]) continue;
 24         int t = lx[x] + ly[y] - mp[x][y];
 25         if (t == 0)//(x,y)在相等子图中
 26         {
 27             visy[y] = 1;
 28             if (match[y] == -1 || dfs(match[y]))
 29             {//注意这里要搜match[y]而不是y,因为我们只搜索x侧的,不需要搜索y侧的 
 30                 match[y] = x;
 31                 return true;
 32             }
 33         }
 34         else if (slack[y]>t) slack[y] = t;//(x,y)不在相等子图中且y不在交错树中slack 取最小的
 35     }
 36     return false;
 37 }
 38 
 39 int KM()
 40 {
 41     memset(match, -1, sizeof(match));
 42     memset(lx, -INF, sizeof(lx));
 43     memset(ly, 0, sizeof(ly));
 44     for (int i = 1; i <= nx; i++)
 45     {
 46         for (int j = 1; j <= ny; j++)
 47         {//lx初始化为与它关联边中最大的
 48             if (mp[i][j]>lx[i]) lx[i] = mp[i][j];
 49         }
 50     }
 51     for (int x = 1; x <= nx; x++)
 52     {
 53         for (int y = 1; y <= ny; y++)
 54             slack[y] = INF; //每次换新的x结点都要初始化slack
 55         while (1)
 56         {
 57             memset(visx, 0, sizeof(visx));
 58             memset(visy, 0, sizeof(visy));//这两个初始化必须放在这里,因此每次dfs()都要更新
 59             if (dfs(x)) break;
 60             //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广
 61             //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,所有在增广轨中的Y方点的标号全部加上一个常数d.
 62             int d = INF;
 63             for (int y = 1; y <= ny; y++)
 64             {
 65                 if (!visy[y] && d>slack[y]) d = slack[y];
 66             }
 67             for (int x = 1; x <= nx; x++)
 68             {
 69                 if (visx[x]) lx[x] -= d;
 70             }
 71             for (int y = 1; y <= ny; y++)
 72             {//修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d,这是因为lx[i] 减小了delta,slack[j] = min(lx[i] + ly[j] -w[i][j]) --j不属于交错树--也需要减少delta,第二类边
 73                 if (visy[y]) ly[y] += d;
 74                 else slack[y] -= d;
 75             }
 76         }
 77     }
 78     int res = 0;
 79     for (int i = 1; i <= ny; i++)
 80     {
 81         if (match[i]>-1) res += mp[match[i]][i];
 82     }
 83     return res;
 84 }
 85 
 86 int main()
 87 {
 88     int n, m;
 89     while (~scanf("%d%d", &n, &m))
 90     {
 91         if (n + m == 0) break;
 92         for (int i = 1; i <= n; i++)
 93         {
 94             scanf("%s", maze[i] + 1);
 95         }
 96         int cnt1 = 0, cnt2 = 0;
 97         for (int i = 1; i <= n; i++)
 98         {
 99             for (int j = 1; j <= m; j++)
100             {
101                 if (maze[i][j] == 'm')
102                 {
103                     sa[++cnt1].a = i;
104                     sa[cnt1].b = j;
105                 }
106                 if (maze[i][j] == 'H')
107                 {
108                     sb[++cnt2].a = i;
109                     sb[cnt2].b = j;
110                 }
111             }
112         }
113         int cnt = cnt1;
114         for (int i = 1; i <= cnt1; i++)
115         {
116             for (int j = 1; j <= cnt2; j++)
117             {
118                 mp[i][j] = abs(sa[i].a - sb[j].a) + abs(sa[i].b - sb[j].b);
119                 mp[i][j] = -mp[i][j];//取反求最大权匹配(也可以用一个极大值减去原来的值求最大权匹配)
120             }
121         }
122         ny = nx = cnt;
123         printf("%dn", -KM());//再取反则为最小权
124     }
125     return 0;
126 }
 1 #include<iostream>
 2 #include<queue>
 3 #include<memory.h>
 4 using namespace std;
 5 int n, p;
 6 const int maxn = 310;//最大学生数
 7 const int maxp = 110;//最大学科数
 8 bool mp[maxp][maxn];//1表示该ij可以匹配
 9 int cx[maxp];//记录x集合中匹配的y元素是哪一个
10 int cy[maxn];//记录y集合中匹配的x元素是哪一个
11 int vis[maxn];//标记该顶点是否访问过
12 bool dfs(int u)
13 {
14     for (int v = 1; v <= n; v++)
15     {
16         if (mp[u][v] && !vis[v])
17         {
18             vis[v] = 1;
19             if (cy[v] == -1 || dfs(cy[v]))//)//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路
20             {
21                 cx[u] = v; cy[v] = u;
22                 return 1;
23             }
24         }
25     }
26     return 0;
27 }
28 int maxmatch()//匈牙利算法主函数
29 {
30     int ans = 0;
31     memset(cx, 0xff, sizeof cx);//初始值为-1表示两个集合中都没有匹配的元素!
32     memset(cy, 0xff, sizeof cy);
33     for (int i = 1; i <= p; i++)
34         if (cx[i] == -1)//如果i未匹配
35         {
36             memset(vis, 0, sizeof(vis));
37             ans += dfs(i);
38         }
39     return ans;
40 }
41 
42 int main()
43 {
44     int t;
45     scanf("%d", &t);
46     while (t--)
47     {
48         scanf("%d%d", &p, &n);
49         memset(mp, 0, sizeof(mp));
50         for (int i = 1; i <= p; i++)
51         {
52             int count;
53             scanf("%d", &count);
54             for (int j = 1; j <= count; j++)
55             {
56                 int v;
57                 scanf("%d", &v);
58                 mp[i][v] = true;
59             }
60        }
61         int ans = maxmatch();
62         if (ans < p)printf("NOn");
63         else printf("YESn");
64     }
65 
66     return 0;
67 }

View Code

View Code

 2、uva 11383 Golden Tiger Claw

3、hdu 1281 棋盘游戏(最大匹配)

  题意:n*n的矩阵,现在需要确定row和col数组,保证在w(i,j)<=row(i)+col(j)的情况下,求row数组和col数组的和的最小值。

  题意:所有棋子不能同行或同列。给出棋子可以放的位置,问最多能放几个,同时给出重要点(去掉某个位置后就无法保证放尽量多的棋子)的个数。

  思路:二分图完美匹配,KM算法。

  思路:x集代表行,y集代表列,对于可以放的位置(x,y)将x和y连一条边。最多能放的棋子即该二分图的最大匹配。接下来每次去掉一个位置看看最大匹配是否不变,如果改变则为重要点。

图片 7图片 8

图片 9图片 10

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 using namespace std;
  5 const int maxn = 510;
  6 const int INF = 0x3f3f3f3f;
  7 int mp[maxn][maxn], match[maxn], lx[maxn], ly[maxn], visx[maxn], visy[maxn], slack[maxn];
  8 int n, nx, ny;
  9 //lx,ly为顶标,nx,ny分别为x点集y点集的个数
 10 //match数组记录右边y端点所连的左端点x,visx,visy数组记录是否曾访问过,也是判断是否在增广路上
 11 //KM求二分图最小匹配模板:只需把权值都变成负的,再用KM算出最大权匹配,然后取反就是答案
 12 
 13 bool dfs(int x)
 14 {
 15     visx[x] = 1;
 16     for (int y = 1; y <= ny; y++)
 17     {
 18         if (visy[y]) continue;
 19         int t = lx[x] + ly[y] - mp[x][y];
 20         if (t == 0)
 21         {
 22             visy[y] = 1;
 23             if (match[y] == -1 || dfs(match[y]))
 24             {
 25                 match[y] = x;
 26                 return true;
 27             }
 28         }
 29         else if (slack[y] > t) slack[y] = t;
 30     }
 31     return false;
 32 }
 33 int KM()
 34 {
 35     //初始化
 36     memset(match, -1, sizeof(match));
 37     memset(lx, - INF, sizeof(lx));
 38     memset(ly, 0, sizeof(ly));
 39     for (int i = 1; i <= nx; i++)
 40     {
 41         for (int j = 1; j <= ny; j++)
 42         {
 43             if (mp[i][j] > lx[i]) lx[i] = mp[i][j];
 44         }
 45     }
 46     for (int x = 1; x <= nx; x++)
 47     {
 48         for (int y = 1; y <= ny; y++) slack[y] = INF;
 49         while (1)
 50         {
 51             memset(visx, 0, sizeof(visx));
 52             memset(visy, 0, sizeof(visy));
 53             if (dfs(x)) break;
 54             int d = INF;
 55             for (int y = 1; y <= ny; y++)
 56             {
 57                 if (!visy[y] && d > slack[y]) d = slack[y];
 58             }
 59             for (int x = 1; x <= nx; x++)
 60             {
 61                 if (visx[x]) lx[x] -= d;
 62             }
 63             for (int y = 1; y <= ny; y++)
 64             {
 65                 if (visy[y]) ly[y] += d;
 66                 else slack[y] -= d;
 67             }
 68         }
 69     }
 70     int res = 0;
 71     for (int i = 1; i <= ny; i++)
 72     {
 73         if (match[i] > -1) res += mp[match[i]][i];
 74     }
 75     return res;
 76 }
 77 int main()
 78 {
 79     while (~scanf("%d", &n))
 80     {
 81         for (int i = 1; i <= n; i++)
 82         {
 83             for (int j = 1; j <= n; j++)
 84             {
 85                 scanf("%d", &mp[i][j]);
 86                 mp[i][j] = mp[i][j];
 87             }
 88         }
 89         nx = ny = n;
 90         int ans = KM();
 91         for (int i = 1; i <= n; i++)
 92         {
 93             if (i > 1) printf(" %d", lx[i]);
 94             else printf("%d", lx[i]);
 95         }
 96         printf("n");
 97         for (int i = 1; i <= n; i++)
 98         {
 99             if (i > 1) printf(" %d", ly[i]);
100             else printf("%d", ly[i]);
101         }
102         printf("n");
103         printf("%dn",ans);
104     }
105     return 0;
106 }
 1 #include<iostream>
 2 #include<queue>
 3 #include<memory.h>
 4 using namespace std;
 5 int n, m, k;
 6 const int maxr = 110;//最大行数
 7 const int maxc = 110;//最大列数
 8 const int maxk = 10010;//最多可放位置
 9 bool mp[maxr][maxc];//1表示该ij可以匹配
10 int cx[maxc];//记录x集合中匹配的y元素是哪一个
11 int cy[maxr];//记录y集合中匹配的x元素是哪一个
12 int vis[maxr];//标记该顶点是否访问过
13 struct point
14 {
15     int x;
16     int y;
17 }points[maxk];
18 bool dfs(int u)
19 {
20     for (int v = 1; v <= m; v++)
21     {
22         if (mp[u][v] && !vis[v])
23         {
24             vis[v] = 1;
25             if (cy[v] == -1 || dfs(cy[v]))//)//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路
26             {
27                 cx[u] = v; cy[v] = u;
28                 return 1;
29             }
30         }
31     }
32     return 0;
33 }
34 int maxmatch()//匈牙利算法主函数
35 {
36     int ans = 0;
37     memset(cx, 0xff, sizeof cx);//初始值为-1表示两个集合中都没有匹配的元素!
38     memset(cy, 0xff, sizeof cy);
39     for (int i = 1; i <= n; i++)
40         if (cx[i] == -1)//如果i未匹配
41         {
42             memset(vis, 0, sizeof(vis));
43             ans += dfs(i);
44         }
45     return ans;
46 }
47 
48 int main()
49 {
50     int Case = 1;
51     while (~scanf("%d%d%d", &n, &m, &k))
52     {
53         memset(mp, 0, sizeof(mp));
54         for (int i = 1; i <= k; i++)
55         {
56             scanf("%d%d", &points[i].x, &points[i].y);
57             mp[points[i].x][points[i].y] = 1;
58         }
59         int ans = maxmatch();
60         int import = 0;
61         for (int i = 1; i <= k; i++)
62         {
63             mp[points[i].x][points[i].y] = 0;
64             int tmp = maxmatch();
65             mp[points[i].x][points[i].y] = 1;
66             if (tmp < ans) import++;
67         }
68         printf("Board %d have %d important blanks for %d chessmen.n", Case++, import, ans);
69     }
70 
71     return 0;
72 }

View Code

View Code

 3、uvalive 2238 Fixed Partition Memory Management

4、HDU 2819 Swap(最大匹配)

  题意:有m个内存,n个程序,每个程序对应不同的内存大小有不同的运行时间。该程序对应已知的内存单元的时间为对应的取下线最接近的时间。求所有程序的总结束时间的平均值最小。

  题意:给出n*n的矩阵,问能否通过交换某两行或某两列若干次后,使得其对角线上元素均为1.

  思路:对于在一个内存中的情况:设该内存中有K个程序。其运行时间分别为t1,t2……tk,则第i个程序结束时间Ti=t1+t2+……+ti,所有程序运行时间之和为kt1+(k-1)t2+(k-2)t3+……+tk。即对于在内存区域j中倒数第p个执行的程序i来说,其对于总的运行时间的贡献为p*Tij,Tij为第i个程序在第j个内存区域内运行的时间。二分图最小权值匹配,KM算法。

  思路:只交换行或只交换列均可达到一样的效果(如果达不到,说明无解)。列既作为x集,也作为y集,[i][j]连线表示将第j列和第i列互换(当[x][y]为1时,将[x][y]连线,表示将可以其换到[x][x])。然后求此二分图的最大匹配。输出每个匹配时,注意交换,否则会多输出。匈牙利算法。

图片 11图片 12

图片 13图片 14

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 using namespace std;
  5 const int maxm = 15;
  6 const int maxn = 55;
  7 const int INF = 0x3f3f3f3f;
  8 int mp[maxn][maxn*maxm],lx[maxn],ly[maxn*maxm],slack[maxn*maxm],match[maxn*maxm],visx[maxn],visy[maxn*maxm];
  9 int m, n,nx,ny;
 10 
 11 int totmem[maxm];
 12 struct node
 13 {
 14     int k, mem[maxm], tm[maxm];
 15 }pg[maxn];
 16 
 17 struct re
 18 {
 19     int id, l, r;
 20 }result[maxn];
 21 bool dfs(int x)
 22 {
 23     visx[x] = 1;
 24     for (int y = 1; y <= ny; y++)
 25     {
 26         if (visy[y])continue;
 27         int t = lx[x] + ly[y] - mp[x][y];
 28         if (t == 0)
 29         {
 30             visy[y] = 1;
 31             if (match[y] == -1 || dfs(match[y]))
 32             {
 33                 match[y] = x;
 34                 return true;
 35             }
 36         }
 37         else if (slack[y] > t) slack[y] = t;
 38     }
 39     return false;
 40 }
 41 
 42 void KM()
 43 {
 44     memset(match, -1, sizeof(match));
 45     memset(lx, -INF, sizeof(lx));
 46     memset(ly, 0, sizeof(ly));
 47     for (int i = 1; i <= nx; i++)
 48     {
 49         for (int j = 1; j <= ny; j++)
 50         {
 51             if (mp[i][j] > lx[i]) lx[i] = mp[i][j];
 52         }
 53     }
 54 
 55     for (int x = 1; x <= nx; x++)
 56     {
 57         for (int y = 1; y <= ny; y++) slack[y] = INF;
 58         while (1)
 59         {
 60             memset(visx, 0, sizeof(visx));
 61             memset(visy, 0, sizeof(visy));
 62 
 63             if (dfs(x)) break;
 64             int d = INF;
 65             for (int y = 1; y <= ny; y++)
 66             {
 67                 if (!visy[y] && d > slack[y]) d = slack[y];
 68             }
 69             for (int xx = 1; xx <= nx; xx++)
 70             {
 71                 if (visx[xx]) lx[xx] -= d;
 72             }
 73             for (int y = 1; y <= ny; y++)
 74             {
 75                 if (visy[y]) ly[y]+= d;
 76                 else slack[y] -= d;
 77             }
 78         }
 79     }
 80 }
 81 
 82 void Oput()
 83 {
 84     int ans = 0;
 85     for (int i = 1; i <= nx; i++) ans += lx[i];
 86     for (int i = 1; i <= ny; i++) ans += ly[i];
 87     printf("Average turnaround time = %.2lfn", n?-1.0*ans / n:0);
 88     int time[11];//记录每个内存当前运行结束时间
 89     memset(time, 0, sizeof(time));
 90     for (int i = 1; i <= m; i++)
 91     {//枚举内存
 92         int p;
 93         for (p = 1; p <= n; p++) if (match[(i - 1)*n + p]==-1) break;//找到该内存的第一个程序位置
 94         for (p = p - 1; p >= 1; --p)
 95         {
 96             int x = match[(i - 1)*n + p];//所对应的程序
 97             result[x].id = i;//程序所对应的内存
 98             result[x].l = time[i];
 99             time[i] += (-mp[x][(i - 1)*n + p] / p);
100             result[x].r = time[i];
101         }
102     }
103     for (int i = 1; i <= n; i++)
104     {
105         printf("Program %d runs in region %d from %d to %dn", i, result[i].id, result[i].l, result[i].r);
106     }
107 }
108 
109 
110 int main()
111 {
112     int Case = 1;
113     while (~scanf("%d%d", &m, &n)&&n&&m)
114     {
115         for (int i = 1; i <= m; i++) scanf("%d", &totmem[i]);
116         for (int i = 1; i <= n; i++)
117         {
118             scanf("%d", &pg[i].k);
119             for (int j = 1; j <= pg[i].k; j++) scanf("%d%d", &pg[i].mem[j], &pg[i].tm[j]);
120         }
121         for (int i = 1; i <= n; i++)
122         {//枚举当前程序
123             for (int j = 1; j <= m; j++)
124             {//枚举当前内存
125                 int tmp;//在当前内存的运行时间
126                 if (totmem[j] < pg[i].mem[1]) tmp = INF;
127                 else
128                 {
129                     int pos;
130                     for (pos = 1; pos <= pg[i].k; pos++)
131                     {
132                         if (pg[i].mem[pos] > totmem[j]) break;
133                     }
134                     tmp = pg[i].tm[pos - 1];
135                 }
136                 for (int k = 1; k <= n; k++)
137                 {//当前程序在当前内存是倒数第k个程序,则对总时间的贡献为k*tmp
138                     if (tmp == INF) mp[i][(j - 1)*n + k] = -INF;
139                     else mp[i][(j - 1)*n + k] = -k*tmp;//求最小权匹配,取负
140                 }
141             }
142         }
143         nx = n, ny = n*m;
144         KM();
145         if (Case > 1) printf("n");
146         printf("Case %dn", Case++);
147         Oput();
148     }
149     return 0;
150 }
 1 #include<iostream>
 2 #include<queue>
 3 #include<memory.h>
 4 #include<algorithm>
 5 using namespace std;
 6 int n;
 7 const int maxr = 110;//最大行数
 8 const int maxc = 110;//最大列数
 9 const int maxk = 1010;//最多可放位置
10 bool mp[maxr][maxc];//1表示该ij可以匹配
11 int cx[maxc];//记录x集合中匹配的y元素是哪一个
12 int cy[maxr];//记录y集合中匹配的x元素是哪一个
13 int vis[maxr];//标记该顶点是否访问过
14 struct stp
15 {
16     int x;
17     int y;
18 }stps[maxk];
19 bool dfs(int u)
20 {
21     for (int v = 1; v <= n; v++)
22     {
23         if (mp[u][v] && !vis[v])
24         {
25             vis[v] = 1;
26             if (cy[v] == -1 || dfs(cy[v]))//)//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路
27             {
28                 cx[u] = v; cy[v] = u;
29                 return 1;
30             }
31         }
32     }
33     return 0;
34 }
35 int maxmatch()//匈牙利算法主函数
36 {
37     int ans = 0;
38     memset(cx, 0xff, sizeof cx);//初始值为-1表示两个集合中都没有匹配的元素!
39     memset(cy, 0xff, sizeof cy);
40     for (int i = 1; i <= n; i++)
41         if (cx[i] == -1)//如果i未匹配
42         {
43             memset(vis, 0, sizeof(vis));
44             ans += dfs(i);
45         }
46     return ans;
47 }
48 
49 int main()
50 {
51     while (~scanf("%d", &n))
52     {
53         memset(mp, 0, sizeof(mp));
54         for (int i = 1; i <= n; i++)
55         {
56             for (int j = 1; j <= n; j++)
57             {
58                 int v;
59                 scanf("%d", &v);
60                 if (v > 0) mp[i][j] = 1;
61             }
62         }
63         int ans = maxmatch();
64         if (ans < n)printf("-1n");
65         else
66         {
67             int t = 0;
68             for (int i = 1; i <= n; i++)
69             {
70                 int j;
71                 for (j = 1; j <= n; j++) if (cy[j] == i)break;
72                 if (i != j)
73                 {
74                     stps[t].x = i, stps[t].y = j;
75                     t++;
76                     swap(cy[i], cy[j]);
77                 }
78             }
79             printf("%dn", t);
80             for (int i = 0; i < t; i++)
81             {
82                 printf("C %d %dn", stps[i].x, stps[i].y);
83             }
84         }
85     }
86 
87     return 0;
88 }

View Code

View Code

 4、uvalive 3989 Ladies' Choice

5、hdu 2389 Rain on your Parade(最大匹配)(Hopcroft-Carp算法模板)

  题意:有n个女孩,n个男孩,现在需要匹配出n对男孩和女孩。对于每个女孩,按照喜欢程度有降序的男生order,对于男生也一样。现在要求以女生优先,保证在能匹配全部的情况下,每个女生的选择都是最好的。

  题意:有m个人,t时刻后会下雨。地上有n把伞,每把伞只能一个人用。给出人和伞的坐标,以及人的速度,问在下雨前,最多能有多少个人拿到伞?

  思路:稳定婚姻问题·稳定匹配·GS算法

  思路:人作为x集,伞作为y集,如果人能够到达伞的位置,把该人的编号和该伞的编号连线。之后求最大匹配。Hopcroft-Carp算法。

图片 15图片 16

图片 17图片 18

 1 //稳定婚姻问题
 2 
 3 #include<iostream>
 4 #include<queue>
 5 #include<cstdio>
 6 using namespace std;
 7 const int maxn = 1010;
 8 
 9 struct gs//Gale - Shapley算法
10 {//女士优先
11     int n;
12     int girl_pre[maxn][maxn];
13     int boy_pre[maxn][maxn];
14     int girl_match[maxn], boy_match[maxn];
15     int next[maxn];
16     queue<int>q;//未开始选择的女孩
17 
18     void engage(int girl, int boy)
19     {//匹配
20         int pre = boy_match[boy];
21         if (pre)
22         {
23             girl_match[pre] = 0;
24             q.push(pre);
25         }
26         girl_match[girl] = boy;
27         boy_match[boy] = girl;
28     }
29 
30     void read()
31     {
32         scanf("%d", &n);
33         for (int i = 1; i <= n; i++)
34         {
35             for (int j = 1; j <= n; j++) scanf("%d", &girl_pre[i][j]);
36             next[i] = 1;//下次等待选择
37             girl_match[i] = 0;
38             q.push(i);//加入未选择的女生队列
39         }
40         for (int i = 1; i <= n; i++)
41         {
42             for (int j = 1; j <= n; j++)
43             {
44                 int p;
45                 scanf("%d", &p);
46                 boy_pre[i][p] = j;//在i男生心中p女孩的排名
47             }
48             boy_match[i] = 0;
49         }
50     }
51 
52     void solve()
53     {
54         while (!q.empty())
55         {
56             int girl = q.front();
57             q.pop();
58             int boy = girl_pre[girl][next[girl]++];
59             if (!boy_match[boy]) engage(girl, boy);//如果该女孩喜欢的男孩没有被选
60             else if (boy_pre[boy][girl] < boy_pre[boy][boy_match[boy]]) engage(girl, boy);//或者该女孩喜欢的男孩的当前匹配的女孩喜欢程度不及该女孩
61             else q.push(girl);//等待下次匹配
62         }
63     }
64 
65     void print()
66     {
67         for (int i = 1; i <= n; i++) printf("%dn", girl_match[i]);
68     }
69 
70 }GS;
71 int main()
72 {
73     int t;
74     scanf("%d", &t);
75     while (t--)
76     {
77         GS.read();
78         GS.solve();
79         GS.print();
80         if (t) printf("n");
81     }
82 
83 
84     return 0;
85 }
  1 #include<iostream>
  2 #include<queue>
  3 #include<memory.h>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 int n,t,m,Nx,Ny,dis;
  8 const int maxn = 3100;//最大行数
  9 const int maxm = 3100;//最大列数
 10 const int maxk = 3100;
 11 const int INF = 0x7fffffff;
 12 bool mp[maxn][maxm];//1表示该ij可以匹配
 13 int cx[maxm];//记录x集合中匹配的y元素是哪一个
 14 int dx[maxm];
 15 int cy[maxn];//记录y集合中匹配的x元素是哪一个
 16 int dy[maxn];
 17 int vis[maxm];//标记该顶点是否访问过
 18 struct point
 19 {
 20     int x;
 21     int y;
 22     int v;
 23 }customer[maxk];
 24 struct point2
 25 {
 26     int x;
 27     int y;
 28 }unbrella[maxk];
 29 bool searchP(void)    //BFS   
 30 {
 31     queue <int> Q;
 32     dis = INF;
 33     memset(dx, -1, sizeof(dx));
 34     memset(dy, -1, sizeof(dy));
 35     for (int i = 1; i <= Nx; i++)
 36         if (cx[i] == -1)
 37         {
 38             Q.push(i); dx[i] = 0;
 39         }
 40     while (!Q.empty())
 41     {
 42         int u = Q.front(); Q.pop();
 43         if (dx[u] > dis) break;        //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充  
 44         for (int v = 1; v <= Ny; v++)
 45             if (mp[u][v] && dy[v] == -1)
 46             {        //v是未匹配点  
 47                 dy[v] = dx[u] + 1;
 48                 if (cy[v] == -1) dis = dy[v];    //得到本次BFS的最大遍历层次  
 49                 else
 50                 {
 51                     dx[cy[v]] = dy[v] + 1;         //v是匹配点,继续延伸  
 52                     Q.push(cy[v]);
 53                 }
 54             }
 55     }
 56     return dis != INF;
 57 }
 58 
 59 bool DFS(int u)
 60 {
 61     for (int v = 1; v <= Ny; v++)
 62         if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1)
 63         {
 64             vis[v] = 1;
 65             if (cy[v] != -1 && dy[v] == dis) continue;   //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。  
 66             if (cy[v] == -1 || DFS(cy[v]))
 67             {     //是增广路径,更新匹配集  
 68                 cy[v] = u; cx[u] = v;
 69                 return 1;
 70             }
 71         }
 72     return 0;
 73 }
 74 
 75 int MaxMatch(void)
 76 {
 77     int res = 0;
 78     memset(cx, -1, sizeof(cx));
 79     memset(cy, -1, sizeof(cy));
 80     while (searchP())
 81     {
 82         memset(vis, 0, sizeof(vis));
 83         for (int i = 1; i <= Nx; i++)
 84             if (cx[i] == -1 && DFS(i)) res++;   //查找到一个增广路径,匹配数res++  
 85     }
 86     return res;
 87 }
 88 
 89 int main()
 90 {
 91     int C;
 92     scanf("%d", &C);
 93     int Case = 1;
 94     while (C--)
 95     {
 96         scanf("%d", &t);
 97         memset(mp, 0, sizeof(mp));
 98         scanf("%d", &m);
 99         for (int i = 1; i <= m; i++)
100         {
101             scanf("%d%d%d", &customer[i].x, &customer[i].y, &customer[i].v);
102         }
103         scanf("%d", &n);
104         for (int i = 1; i <= n; i++)
105         {
106             scanf("%d%d", &unbrella[i].x, &unbrella[i].y);
107         }
108         for (int i = 1; i <= n; i++)
109         {
110             for (int j = 1; j <= m; j++)
111             {
112                 double dis = sqrt((unbrella[i].x - customer[j].x)*(unbrella[i].x - customer[j].x) + (unbrella[i].y - customer[j].y)*(unbrella[i].y - customer[j].y));
113                 if (dis <= t*customer[j].v) mp[i][j] = true;
114             }
115         }
116         Nx = n, Ny = m;
117         int ans = MaxMatch();
118         printf("Scenario #%d:n", Case++);
119         printf("%dnn", ans);
120     }
121     return 0;
122 }

View Code

View Code

 5、uva 11419 SAM I AM

6、hdu 4185 Oil Skimming

  题意:有R·C的网格,存在N个敌人,现在有一把武器,每一次发射可以消灭一行或一列的敌人,问最少的发射次数,并且给出发射所在的行和列。

  题意:给出一张图,每相邻两个油田可以建立一道沟渠。问最大沟渠的数目。

  思路:首先这是一道求最小点覆盖(用最少的点覆盖所有的边)和输出最小点覆盖集的问题。最小点覆盖数=最大匹配数。怎么求最小点覆盖集呢?详见代码注释~

  思路:给每一个油田编号。油田既作为x集,又作为y集,如果两块油田相邻,则连线。之后求最大匹配。

图片 19图片 20

图片 21图片 22

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn = 1010;
 6 bool mp[maxn][maxn];
 7 int L_match[maxn], R_match[maxn], visL[maxn], visR[maxn];
 8 int n, nl, nr;
 9 bool dfs(int lx)
10 {
11     visL[lx] = true;
12     for (int ly = 1; ly <= nr; ly++)
13     {
14         if (mp[lx][ly] && !visR[ly])
15         {
16             visR[ly] = true;
17             if (!R_match[ly] || dfs(R_match[ly]))
18             {//如果当前的ly没有被匹配,或者已经匹配但是和ly匹配的lx'能够和其他ly'匹配,那么当前ly和lx就可以匹配
19                 L_match[lx] = ly;
20                 R_match[ly] = lx;
21                 return true;
22             }
23         }
24     }
25     return false;
26 }
27 
28 int maxmatch_XYL()
29 {
30     int ans = 0;
31     memset(L_match, 0, sizeof(L_match));
32     memset(R_match, 0, sizeof(R_match));
33     for (int i = 1; i <= nl; i++)
34     {
35         memset(visL, 0, sizeof(visL));
36         memset(visR, 0, sizeof(visR));
37         if (dfs(i)) ans++;
38     }
39     return ans;
40 }
41 int main()
42 {
43     while (~scanf("%d%d%d", &nl, &nr, &n))
44     {
45         if (nl == 0 && nr == 0 && n == 0) break;
46         memset(mp, 0, sizeof(mp));
47         for (int i = 1; i <= n; i++)
48         {
49             int x, y;
50             scanf("%d%d", &x, &y);
51             mp[x][y] = true;
52         }
53         int ans = maxmatch_XYL();
54         printf("%d", ans);
55         //寻找最小点覆盖集并输出
56         memset(visL, 0, sizeof(visL));
57         memset(visR, 0, sizeof(visR));
58         //从当前未匹配的lx开始dfs,如果有边a(说明原本假设的最小点覆盖集<即已匹配的lx>不能覆盖到当前dfs到的这一条边),那么原本和边a连接的lx'需更改为a所连接的ly'
59         for (int i = 1; i <= nl; i++) if (!L_match[i]) dfs(i);
60         for (int i = 1; i <= nl; i++) if (!visL[i]) printf(" r%d", i);//在初期假设的最小点覆盖集中无需删的点
61         for (int i = 1; i <= nr; i++) if (visR[i]) printf(" c%d", i);//在dfs过程中lx'更换成的ly'
62         printf("n");
63     }
64     return 0;
65 }
  1 #include<iostream>
  2 #include<queue>
  3 #include<memory.h>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 int n, t, m, Nx, Ny, dis;
  8 const int maxn = 650;//最大行数
  9 const int maxm = 650;//最大列数
 10 const int maxk = 360005;
 11 const int INF = 0x7fffffff;
 12 char M[maxn][maxm];
 13 bool mp[maxn][maxn];//1表示该ij可以匹配
 14 int cx[maxk];//记录x集合中匹配的y元素是哪一个
 15 int dx[maxk];
 16 int cy[maxk];//记录y集合中匹配的x元素是哪一个
 17 int dy[maxk];
 18 int vis[maxk];//标记该顶点是否访问过
 19 struct point
 20 {
 21     int x;
 22     int y;
 23 }oils[maxk];
 24 bool searchP(void)    //BFS   
 25 {
 26     queue <int> Q;
 27     dis = INF;
 28     memset(dx, -1, sizeof(dx));
 29     memset(dy, -1, sizeof(dy));
 30     for (int i = 1; i <= Nx; i++)
 31         if (cx[i] == -1)
 32         {
 33             Q.push(i); dx[i] = 0;
 34         }
 35     while (!Q.empty())
 36     {
 37         int u = Q.front(); Q.pop();
 38         if (dx[u] > dis) break;        //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充  
 39         for (int v = 1; v <= Ny; v++)
 40             if (mp[u][v] && dy[v] == -1)
 41             {        //v是未匹配点  
 42                 dy[v] = dx[u] + 1;
 43                 if (cy[v] == -1) dis = dy[v];    //得到本次BFS的最大遍历层次  
 44                 else
 45                 {
 46                     dx[cy[v]] = dy[v] + 1;         //v是匹配点,继续延伸  
 47                     Q.push(cy[v]);
 48                 }
 49             }
 50     }
 51     return dis != INF;
 52 }
 53 
 54 bool DFS(int u)
 55 {
 56     for (int v = 1; v <= Ny; v++)
 57         if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1)
 58         {
 59             vis[v] = 1;
 60             if (cy[v] != -1 && dy[v] == dis) continue;   //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。  
 61             if (cy[v] == -1 || DFS(cy[v]))
 62             {     //是增广路径,更新匹配集  
 63                 cy[v] = u; cx[u] = v;
 64                 return 1;
 65             }
 66         }
 67     return 0;
 68 }
 69 
 70 int MaxMatch(void)
 71 {
 72     int res = 0;
 73     memset(cx, -1, sizeof(cx));
 74     memset(cy, -1, sizeof(cy));
 75     while (searchP())
 76     {
 77         memset(vis, 0, sizeof(vis));
 78         for (int i = 1; i <= Nx; i++)
 79             if (cx[i] == -1 && DFS(i)) res++;   //查找到一个增广路径,匹配数res++  
 80     }
 81     return res;
 82 }
 83 
 84 int main()
 85 {
 86     int C,N;
 87     scanf("%d", &C);
 88     int Case = 1;
 89     while (C--)
 90     {
 91         scanf("%d", &N);
 92         memset(mp, 0, sizeof(mp));
 93         int cnt = 0;
 94         for (int i = 1; i <= N; i++)
 95         {
 96             for (int j = 1; j <= N; j++)
 97             {
 98                 cin >> M[i][j];
 99                 if (M[i][j] == '#')
100                 {
101                     oils[cnt].x = i;
102                     oils[cnt].y = j;
103                     cnt++;
104                 }
105             }
106         }
107         for (int i = 0; i < cnt; i++)
108         {
109             for (int j = 0; j < cnt; j++)
110             {
111                 if (abs(oils[i].x - oils[j].x) + abs(oils[i].y - oils[j].y) == 1)
112                 {
113                     mp[i+1][j+1] = 1;
114                 }
115             }
116         }
117         Nx = cnt, Ny = cnt;
118         int ans = MaxMatch();
119         printf("Case %d: %dn", Case++,ans/2);
120     }
121     return 0;
122 }

View Code

View Code

 6、uvalive 3415 Guardian of Decency

7、poj 3020 Antenna Placement(无向二分图的最小路径覆盖)

  题意:如果两个人之间身高差大于40或者同一性别或者喜欢不同的音乐或者喜欢相同的运动,则这两个人可以同时带去出游。问当带出的所有人两两之间至少满足其中一个条件下,所能带走的最多的人?

  题意:每个天线最多只能覆盖自己所在的格子和一个相邻的位置(可以是城市也可以是空地)。问最少的天线数目。

  思路:如果两个人4个条件都不符合,那么肯定不能同时带走。现在需要求能带走的最多,则需要最小化不能带走的。当4个条件都不符合时,两人建边,求所选的人两两之间不能连边,即最大独立集问题(选择尽量多的结点,使得任意一条边的两个端点不会同时被选中)。

  思路:把每个城市拆成两点,让其既属于x集又属于y集,然后把相邻的城市连线。计算最大匹配,然后用总城市数-最大匹配数即得最小路径覆盖(用最少的边,覆盖所有顶点。每个顶点初始各有一条边,如果两个点相连,则为了使答案中的每条边都不能交于相同一点,所以要减1.最后即减去最大匹配数).

图片 23图片 24

图片 25图片 26

  1 #include<iostream>
  2 #include<map>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<string>
  6 #include<cmath>
  7 using namespace std;
  8 const int maxn = 510;
  9 map<string, int>music;
 10 map<string, int>sports;
 11 struct per
 12 {
 13     int h;
 14     bool isboy;
 15     int id_music;
 16     int id_sports;
 17 }pp[maxn];
 18 
 19 int n,nl, nr;
 20 bool mp[maxn][maxn];
 21 
 22 int l_match[maxn], r_match[maxn], visl[maxn], visr[maxn];
 23 
 24 bool dfs(int lx)
 25 {
 26     visl[lx] = true;
 27     for (int ly = 1; ly <= nr; ly++)
 28     {
 29         if (mp[lx][ly] && !visr[ly])
 30         {
 31             visr[ly] = true;
 32             if (!r_match[ly] || dfs(r_match[ly]))
 33             {
 34                 l_match[lx] = ly;
 35                 r_match[ly] = lx;
 36                 return true;
 37             }
 38         }
 39     }
 40     return false;
 41 }
 42 
 43 int maxmatch_XYL()
 44 {
 45     int ans = 0;
 46     memset(l_match, 0, sizeof(l_match));
 47     memset(r_match, 0, sizeof(r_match));
 48     for (int i = 1; i <= nl; i++)
 49     {
 50         memset(visl, 0, sizeof(visl));
 51         memset(visr, 0, sizeof(visr));
 52         if (dfs(i)) ans++;
 53     }
 54     return ans;
 55 }
 56 
 57 bool check(int u, int v)
 58 {//四个条件都不满足建边
 59     if (abs(pp[u].h - pp[v].h) > 40)
 60         return false;
 61     else if(pp[u].isboy == pp[v].isboy)
 62         return false;
 63     else if(pp[u].id_music != pp[v].id_music)
 64         return false; 
 65     else if(pp[u].id_sports == pp[v].id_sports) 
 66         return false;
 67     else return true;
 68 }
 69 int main()
 70 {
 71     int t;
 72     scanf("%d", &t);
 73     while (t--)
 74     {
 75         scanf("%d", &n);
 76         nl = nr = n;
 77         memset(mp, 0, sizeof(mp));
 78         music.clear();
 79         sports.clear();
 80         char tmp[110];
 81         for (int i = 1; i <= n; i++)
 82         {
 83             scanf("%d%s", &pp[i].h,tmp);
 84             if (tmp[0] == 'M') pp[i].isboy = true;
 85             else pp[i].isboy = false;
 86             scanf("%s", tmp);
 87             if (!music[tmp]) music[tmp] = music.size() + 1;
 88             pp[i].id_music = music[tmp];
 89             scanf("%s", tmp);
 90             if (!sports[tmp]) sports[tmp] = sports.size() + 1;
 91             pp[i].id_sports = sports[tmp];
 92         }
 93         for (int i = 1; i <= nl; i++)
 94         {
 95             for (int j = i + 1; j <= nr; j++)
 96             {
 97                 if (check(i, j)) mp[i][j]=mp[j][i]=true;
 98             }
 99         }
100         int ans = maxmatch_XYL();
101         printf("%dn", n-ans/2);
102     }
103     return 0;
104 }
  1 #include<iostream>
  2 #include<queue>
  3 #include<memory.h>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 int n, t, m, Nx, Ny, dis,totalr,totalc;
  8 const int maxn = 50;//最大行数
  9 const int maxm = 15;//最大列数
 10 const int maxk = 1000;
 11 const int INF = 0x7fffffff;
 12 int M[maxn][maxm];
 13 bool mp[maxk][maxk];//1表示该ij可以匹配
 14 int cx[maxk];//记录x集合中匹配的y元素是哪一个
 15 int dx[maxk];
 16 int cy[maxk];//记录y集合中匹配的x元素是哪一个
 17 int dy[maxk];
 18 int vis[maxk];//标记该顶点是否访问过
 19 int dr[] = { 0,0,1,-1 };
 20 int dc[] = { 1,-1,0,0 };
 21 struct point
 22 {
 23     int x;
 24     int y;
 25 }oils[maxk];
 26 bool searchP(void)    //BFS   
 27 {
 28     queue <int> Q;
 29     dis = INF;
 30     memset(dx, -1, sizeof(dx));
 31     memset(dy, -1, sizeof(dy));
 32     for (int i = 1; i <= Nx; i++)
 33         if (cx[i] == -1)
 34         {
 35             Q.push(i); dx[i] = 0;
 36         }
 37     while (!Q.empty())
 38     {
 39         int u = Q.front(); Q.pop();
 40         if (dx[u] > dis) break;        //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充  
 41         for (int v = 1; v <= Ny; v++)
 42             if (mp[u][v] && dy[v] == -1)
 43             {        //v是未匹配点  
 44                 dy[v] = dx[u] + 1;
 45                 if (cy[v] == -1) dis = dy[v];    //得到本次BFS的最大遍历层次  
 46                 else
 47                 {
 48                     dx[cy[v]] = dy[v] + 1;         //v是匹配点,继续延伸  
 49                     Q.push(cy[v]);
 50                 }
 51             }
 52     }
 53     return dis != INF;
 54 }
 55 
 56 bool DFS(int u)
 57 {
 58     for (int v = 1; v <= Ny; v++)
 59         if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1)
 60         {
 61             vis[v] = 1;
 62             if (cy[v] != -1 && dy[v] == dis) continue;   //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。  
 63             if (cy[v] == -1 || DFS(cy[v]))
 64             {     //是增广路径,更新匹配集  
 65                 cy[v] = u; cx[u] = v;
 66                 return 1;
 67             }
 68         }
 69     return 0;
 70 }
 71 
 72 int MaxMatch()
 73 {
 74     int res = 0;
 75     memset(cx, -1, sizeof(cx));
 76     memset(cy, -1, sizeof(cy));
 77     while (searchP())
 78     {
 79         memset(vis, 0, sizeof(vis));
 80         for (int i = 1; i <= Nx; i++)
 81             if (cx[i] == -1 && DFS(i)) res++;   //查找到一个增广路径,匹配数res++  
 82     }
 83     return res;
 84 }
 85 
 86 int main()
 87 {
 88     int C, N;
 89     scanf("%d", &C);
 90     int Case = 1;
 91     while (C--)
 92     {
 93         scanf("%d%d", &totalr,&totalc);
 94         memset(mp, 0, sizeof(mp));
 95         int cnt = 0;
 96         for (int i = 1; i <= totalr; i++)
 97         {
 98             for (int j = 1; j <= totalc; j++)
 99             {
100                 char c;
101                 cin >> c;
102                 if (c == 'o')
103                 {
104                     M[i][j] = 0;
105                 }
106                 else
107                 {
108                     M[i][j] = ++cnt;
109                 }
110             }
111         }
112         // //通过“拆点”操作,把每一个城市拆分为2个,分别属于所构造的二分图的两个点集  
113         for (int i = 1; i <= totalr; i++)
114         {
115             for (int j = 1; j <= totalc; j++)
116             {
117                 if (M[i][j] > 0)
118                 {
119                     int u = M[i][j];
120                     for (int k = 0; k < 4; k++)
121                     {
122                         int tr = i + dr[k];
123                         int tc = j + dc[k];
124                         if (tr >= 1 && tr <= totalr&&tc >= 1 && tc <= totalc)
125                         {
126                             if (M[tr][tc] > 0)
127                             {
128                                 mp[u][M[tr][tc]] = true;
129                             }
130                         }
131                     }
132                 }
133             }
134         }
135 
136         Nx = cnt, Ny = cnt;
137         int ans = MaxMatch();
138         printf("%dn", cnt-ans / 2);//无向二分图:最小路径覆盖数 = "拆点"前原图的顶点数 - 最大匹配数/2  
139     }
140     return 0;
141 }

View Code

View Code

 7、uvalive 3126 Taxi Cab Scheme

8、hdu 1054 Strategic Game POJ1463

  题意:有n个预约,记录其预约出租车的时间、起始位置、终点位置,并且一辆出租同时只能接送一个预约的乘客。问最少需要多少出租车?

  题意:有一棵树,现在需要在树上结点的位置放士兵,每个士兵能够守卫与其所在顶点相连接的边。问最少放多少士兵,能够守卫所有的边。

  思路:如果一辆出租车从第i个预约结束后能赶到第j个预约的地点,且至少能够提前1分钟到,则两点连线。整个问题为有向无环图的最小路径覆盖问题。

  思路:每个点既属于x集,又属于y集,两点之间若有边则连线。计算最大匹配。(如果a和b、c、d相连,那么如果选择a和b相连的这条边,则不能再选a点,也就不能再选和a连接的边)

最小路径覆盖参考:

图片 27图片 28

图片 29图片 30

  1 #include<iostream>
  2 #include<queue>
  3 #include<memory.h>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 int n, t, m, Nx, Ny, dis, totalr, totalc;
  8 const int maxn = 50;//最大行数
  9 const int maxm = 15;//最大列数
 10 const int maxk = 1505;
 11 const int INF = 0x7fffffff;
 12 bool mp[maxk][maxk];//1表示该ij可以匹配
 13 int cx[maxk];//记录x集合中匹配的y元素是哪一个
 14 int dx[maxk];
 15 int cy[maxk];//记录y集合中匹配的x元素是哪一个
 16 int dy[maxk];
 17 int vis[maxk];//标记该顶点是否访问过
 18 bool searchP(void)    //BFS   
 19 {
 20     queue <int> Q;
 21     dis = INF;
 22     memset(dx, -1, sizeof(dx));
 23     memset(dy, -1, sizeof(dy));
 24     for (int i = 1; i <= Nx; i++)
 25         if (cx[i] == -1)
 26         {
 27             Q.push(i); dx[i] = 0;
 28         }
 29     while (!Q.empty())
 30     {
 31         int u = Q.front(); Q.pop();
 32         if (dx[u] > dis) break;        //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充  
 33         for (int v = 1; v <= Ny; v++)
 34             if (mp[u][v] && dy[v] == -1)
 35             {        //v是未匹配点  
 36                 dy[v] = dx[u] + 1;
 37                 if (cy[v] == -1) dis = dy[v];    //得到本次BFS的最大遍历层次  
 38                 else
 39                 {
 40                     dx[cy[v]] = dy[v] + 1;         //v是匹配点,继续延伸  
 41                     Q.push(cy[v]);
 42                 }
 43             }
 44     }
 45     return dis != INF;
 46 }
 47 
 48 bool DFS(int u)
 49 {
 50     for (int v = 1; v <= Ny; v++)
 51         if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1)
 52         {
 53             vis[v] = 1;
 54             if (cy[v] != -1 && dy[v] == dis) continue;   //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。  
 55             if (cy[v] == -1 || DFS(cy[v]))
 56             {     //是增广路径,更新匹配集  
 57                 cy[v] = u; cx[u] = v;
 58                 return 1;
 59             }
 60         }
 61     return 0;
 62 }
 63 
 64 int MaxMatch()
 65 {
 66     int res = 0;
 67     memset(cx, -1, sizeof(cx));
 68     memset(cy, -1, sizeof(cy));
 69     while (searchP())
 70     {
 71         memset(vis, 0, sizeof(vis));
 72         for (int i = 1; i <= Nx; i++)
 73             if (cx[i] == -1 && DFS(i)) res++;   //查找到一个增广路径,匹配数res++  
 74     }
 75     return res;
 76 }
 77 
 78 int main()
 79 {
 80     while (~scanf("%d",&n))
 81     {
 82         memset(mp, 0, sizeof(mp));
 83         int cnt = 0,cur,num;
 84         char c;
 85         for (int i = 1; i <= n; i++)
 86         {
 87             cin >> cur >> c >> c >> num >> c;
 88             cur = cur + 1;
 89             for (int j = 1; j <= num; j++)
 90             {
 91                 int v;
 92                 cin >> v;
 93                 v = v + 1;
 94                 mp[cur][v] = true;
 95                 mp[v][cur] = true;
 96             }
 97         }
 98         
 99 
100         Nx = n, Ny = n;
101         int ans = MaxMatch();
102         printf("%dn", ans / 2);
103     }
104     return 0;
105 }
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 int n, nl, nr;
 8 const int maxn = 510;
 9 bool mp[maxn][maxn];
10 int l_match[maxn], r_match[maxn], visl[maxn], visr[maxn];
11 bool dfs(int lx)
12 {
13     visl[lx] = true;
14     for (int ly = 1; ly <= nr; ly++)
15     {
16         if (mp[lx][ly] && !visr[ly])
17         {
18             visr[ly] = true;
19             if (!r_match[ly] || dfs(r_match[ly]))
20             {
21                 l_match[lx] = ly;
22                 r_match[ly] = lx;
23                 return true;
24             }
25         }
26     }
27     return false;
28 }
29 
30 int maxmatch()
31 {
32     int ans = 0;
33     memset(l_match, 0, sizeof(l_match));
34     memset(r_match, 0, sizeof(r_match));
35     for (int i = 1; i <= nl; i++)
36     {
37         memset(visl, 0, sizeof(visl));
38         memset(visr, 0, sizeof(visr));
39         if (dfs(i)) ans++;
40     }
41     return ans;
42 }
43 
44 struct node
45 {
46     int time;
47     int start[2];
48     int end[2];
49     int cost;
50 }ps[maxn];
51 
52 bool check(int from, int to)
53 {
54     if (ps[from].time + ps[from].cost + abs(ps[to].start[0] - ps[from].end[0]) + abs(ps[to].start[1] - ps[from].end[1]) + 1 <= ps[to].time) return true;
55     else return false;
56 }
57 int main()
58 {
59     int t;
60     scanf("%d", &t);
61     while (t--)
62     {
63         memset(mp, 0, sizeof(mp));
64         scanf("%d",&n);
65         nl = nr = n;
66         for (int i = 1; i <= n; i++)
67         {
68             int hh, mm;
69             char c;
70             scanf("%d%c%d", &hh, &c, &mm);
71             ps[i].time = hh * 60 + mm;
72             scanf("%d%d", &ps[i].start[0], &ps[i].start[1]);
73             scanf("%d%d", &ps[i].end[0], &ps[i].end[1]);
74             ps[i].cost = abs(ps[i].start[0] - ps[i].end[0]) + abs(ps[i].start[1] - ps[i].end[1]);
75         }
76         for (int i = 1; i <= n; i++)
77         {
78             for (int j = 1; j <= n; j++)
79             {
80                 if (i!=j&&check(i, j)) mp[i][j]  = true;
81             }
82         }
83         int ans = maxmatch();
84         printf("%dn", n - ans);
85     }
86     return 0;
87 }

View Code

View Code

9、hdu 1151/poj 1422 Air Raid

 8、uva 11248 Frequency Hopping

  题意:有一有向无环图,如果在一个结点放士兵,那么其也能够守卫与其相连的有向边能到达的结点。问最少需要多少守卫能够守卫所有结点。

  题意:有n个基站,用有向边相连,问是否可以从1到N有流量至少为C的最大流?没有的话,是否可以增大一条边的容量而达到目的?

  思路:同上一题,不过注意有向边。

  思路:dicnic求最大流。增大的边肯定选择最小割中的边。在这之后,从求完最大流后的残余网络求其是否满足条件。

图片 31图片 32

 

 1 #include<iostream>
 2 #include<queue>
 3 #include<memory.h>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 int n, t, m, Nx, Ny, dis, totalr, totalc;
 8 const int maxn = 50;//最大行数
 9 const int maxm = 15;//最大列数
10 const int maxk = 125;//x集与y集最多元素
11 const int INF = 0x7fffffff;
12 bool mp[maxk][maxk];//1表示该ij可以匹配
13 int cx[maxk];//记录x集合中匹配的y元素是哪一个
14 int dx[maxk];
15 int cy[maxk];//记录y集合中匹配的x元素是哪一个
16 int dy[maxk];
17 int vis[maxk];//标记该顶点是否访问过
18 bool searchP(void)    //BFS   
19 {
20     queue <int> Q;
21     dis = INF;
22     memset(dx, -1, sizeof(dx));
23     memset(dy, -1, sizeof(dy));
24     for (int i = 1; i <= Nx; i++)
25         if (cx[i] == -1)
26         {
27             Q.push(i); dx[i] = 0;
28         }
29     while (!Q.empty())
30     {
31         int u = Q.front(); Q.pop();
32         if (dx[u] > dis) break;        //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充  
33         for (int v = 1; v <= Ny; v++)
34             if (mp[u][v] && dy[v] == -1)
35             {        //v是未匹配点  
36                 dy[v] = dx[u] + 1;
37                 if (cy[v] == -1) dis = dy[v];    //得到本次BFS的最大遍历层次  
38                 else
39                 {
40                     dx[cy[v]] = dy[v] + 1;         //v是匹配点,继续延伸  
41                     Q.push(cy[v]);
42                 }
43             }
44     }
45     return dis != INF;
46 }
47 
48 bool DFS(int u)
49 {
50     for (int v = 1; v <= Ny; v++)
51         if (!vis[v] && mp[u][v] && dy[v] == dx[u] + 1)
52         {
53             vis[v] = 1;
54             if (cy[v] != -1 && dy[v] == dis) continue;   //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。  
55             if (cy[v] == -1 || DFS(cy[v]))
56             {     //是增广路径,更新匹配集  
57                 cy[v] = u; cx[u] = v;
58                 return 1;
59             }
60         }
61     return 0;
62 }
63 
64 int MaxMatch()
65 {
66     int res = 0;
67     memset(cx, -1, sizeof(cx));
68     memset(cy, -1, sizeof(cy));
69     while (searchP())
70     {
71         memset(vis, 0, sizeof(vis));
72         for (int i = 1; i <= Nx; i++)
73             if (cx[i] == -1 && DFS(i)) res++;   //查找到一个增广路径,匹配数res++  
74     }
75     return res;
76 }
77 
78 int main()
79 {
80     int C;
81     scanf("%d", &C);
82     while (C--)
83     {
84         scanf("%d%d", &n, &m);
85         memset(mp, 0, sizeof(mp));
86         for (int i = 1; i <= m; i++)
87         {
88             int u, v;
89             scanf("%d%d", &u, &v);
90             mp[u][v] = true;
91         }
92         Nx = n, Ny = n;
93         int ans = MaxMatch();
94         printf("%dn", n-ans);
95     }
96     return 0;
97 }

图片 33图片 34

View Code

  1 #include<iostream>
  2 #include<queue>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<cstdio>
  6 #include<vector>
  7 using namespace std;
  8 //Dicnic模板
  9 //最小割中,正向边的容量=其流量,逆向边的流量=0。且最小割中所有正向边的容量之和=最大流
 10 const int maxn = 110;
 11 const int maxe = 10010*2;
 12 const int INF = 0x3f3f3f3f;
 13 struct edge
 14 {
 15     int from, to, cap, flow,next;//边的起点、终点、容量、流量、同起点的下一条边的编号
 16     edge(int ff=0,int tt=0,int cc=0,int fw=0,int nn=0):from(ff),to(tt),cap(cc),flow(fw),next(nn){ }
 17     friend bool operator <(const edge&a, const edge&b)
 18     {
 19         if (a.from == b.from)
 20         {
 21             if (a.to < b.to) return true;
 22             else return false;
 23         }
 24         else if (a.from < b.from) return true;
 25         else return false;
 26     }
 27 }Edge[maxe];
 28 int Head[maxn],tmp_head[maxn],totedge;
 29 vector<edge>ans;
 30 
 31 struct Dicnic
 32 {
 33     int n;
 34     bool vis[maxn];
 35     int level[maxn];
 36     int st, ed;
 37     vector<int>mincut;
 38     int needflow;
 39 
 40     void Init(int nodes,int source,int dest)
 41     {
 42         n = nodes, st = source, ed = dest;
 43         memset(Head, -1, sizeof(Head));
 44         totedge = 0;
 45     }
 46 
 47     void addedge(int from, int to, int cap)
 48     {
 49         Edge[totedge] = edge(from, to, cap, 0, Head[from]);
 50         Head[from] = totedge++;
 51         Edge[totedge] = edge(to, from, 0, 0, Head[to]);
 52         Head[to] = totedge++;
 53     }
 54 
 55     bool Dicnic_bfs()//生成层次图
 56     {
 57         queue<int>q;
 58         int i, u, v;
 59         memset(level, -1, sizeof(level));
 60         memset(vis, 0, sizeof(vis));
 61 
 62         q.push(st);
 63         level[st] = 0;
 64         vis[st] = true;
 65         while (!q.empty())
 66         {
 67             u = q.front();
 68             q.pop();
 69             if (u == ed) return true;
 70             for (int i = Head[u]; i != -1; i = Edge[i].next)
 71             {
 72                 v = Edge[i].to;
 73                 if (Edge[i].cap > Edge[i].flow && !vis[v] && level[v] == -1)
 74                 {//保证这条边有剩余容留,属于残量网络
 75                     vis[v] = true;
 76                     level[v] = level[u] + 1;
 77                     q.push(v);
 78                 }
 79             }
 80         }
 81         return false;
 82     }
 83 
 84     int Dinic_dfs(int u, int maxf)
 85     {
 86         if (u == ed||maxf==0) return maxf;
 87 
 88         int flow = 0,f;
 89         for (int& i = tmp_head[u]; i != -1; i = Edge[i].next)
 90         {
 91             int v = Edge[i].to;
 92             if (Edge[i].cap - Edge[i].flow > 0 && level[v] == level[u] + 1)
 93             {
 94                 f = Dinic_dfs(v, min(maxf, Edge[i].cap - Edge[i].flow));
 95                 if (f > 0)
 96                 {
 97                     Edge[i].flow += f;
 98                     Edge[i ^ 1].flow -= f;
 99                     flow += f;
100                     maxf -= f;
101                     if (0 == maxf) break;
102                     if (flow >= needflow) return flow;
103                 }
104             }
105         }
106         return flow;
107     }
108 
109     int Dinic_maxflow(int needf=INF)//求最大流
110     {
111         int ret = 0;
112         needflow = needf;//需要的流
113         while (Dicnic_bfs())
114         {
115             memcpy(tmp_head, Head, sizeof(Head));
116             ret += Dinic_dfs(st, INF);
117             if (ret >= needflow) return ret;
118         }
119         return ret;
120     }
121 
122     void GetMincut()//获得最小割
123     {
124         mincut.clear();
125         for (int i = 0; i < totedge; i++)
126         {
127             if (vis[Edge[i].from] && !vis[Edge[i].to] && Edge[i].cap > 0) mincut.push_back(i);
128         }
129     }
130 
131     void Reduce()//求剩余容量
132     {
133         for (int i = 0; i < totedge; i++) Edge[i].cap -= Edge[i].flow;
134     }
135 
136     void Clearflow()//把当前流量清零
137     {
138         for (int i = 0; i < totedge; i++) Edge[i].flow = 0;
139     }
140 
141 }dnc;
142 int main()
143 {
144     int N, E, C;
145     int Case = 1;
146     while (~scanf("%d%d%d", &N, &E, &C))
147     {
148         if (N == 0 && E == 0 && C == 0) break;
149         dnc.Init(N, 1, N);
150         for (int i = 1; i <= E; i++)
151         {
152             int from, to, cap;
153             scanf("%d%d%d", &from, &to, &cap);
154             dnc.addedge(from, to, cap);
155         }
156         int flow = dnc.Dinic_maxflow(C);
157         printf("Case %d: ", Case++);
158         if (flow >= C) printf("possiblen");
159         else
160         {
161             dnc.GetMincut();
162             dnc.Reduce();
163             ans.clear();
164             int sz = dnc.mincut.size();
165             for (int i = 0; i < sz; i++)
166             {
167                 edge &tmp = Edge[dnc.mincut[i]];
168                 tmp.cap = C;
169                 dnc.Clearflow();
170                 if (flow + dnc.Dinic_maxflow(C - flow) >= C) ans.push_back(tmp);
171                 tmp.cap =0;
172             }
173             if (ans.empty()) printf("not possiblen");
174             else
175             {
176                 sort(ans.begin(), ans.end());
177                 printf("possible option:");
178                 sz = ans.size();
179                 for (int i = 0; i < sz; i++)
180                 {
181                     if (i) printf(",");
182                     printf("(%d,%d)", ans[i].from, ans[i].to);
183                 }
184                 printf("n");
185             }
186         }
187     }
188     return 0;
189 }

10、poj 2594 Treasure Exploration(最小路径覆盖,可重点)

本文由优盈娱乐发布于音乐,转载请注明出处:大白叔叔专题之匹配、网络流(二)(第一题不

相关阅读