题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6808
网络流之第一次遇见你 ——>基本厚脸皮照搬
感谢HY大佬及参考文献の指点
尽可能写了一点注释方便理解
参考文献:https://blog.csdn.net/weixin_43828245/article/details/107700724
*转载请附链接 谢谢
1 #include<iostream>
2 #include<queue>
3 #include<algorithm>
4 #include<vector>
5 #include<iomanip>
6 #include<cstdio>
7 #include<cmath>
8 #include<cstring>
9
10 #define INF 2e9
11 const int maxn = 2e6 + 10;
12 using namespace std;
13
14 typedef struct point
15 {
16 int flow;
17 int to;
18 int next;
19 point(int a = 0, int b = 0, int c = 0){flow = a, to = b, next = c;}
20 } node;
21
22 int n, cnt;
23 int s, t;//源/汇点
24 int a[maxn], b[maxn];
25 int da[maxn], db[maxn];
26 int head[maxn];//x元素在N数组中的下标即为head[x]
27 node N[maxn];
28
29 int deep[maxn];//各点深度
30 int hhead[maxn];//dfs中更新headの复制品
31
32 void Add_Edge(int i, int j, int val)
33 {
34 N[cnt].flow = val;
35 N[cnt].to = j;
36 N[cnt].next = head[i];
37 head[i] = cnt++;//从零开始,自增为后缀
38 }
39
40 int BFS(int x)//分层网络
41 {
42 memset(deep, 0, sizeof(deep));//除源点外其余点深度为0
43 memcpy(hhead, head ,sizeof(hhead));
44 deep[x] = 1;//源点深度为1
45 queue<int> q;
46 q.push(x);
47 while(!q.empty()){
48 int now = q.front();//取出队首元素后弹出
49 q.pop();
50 for(int i = head[now] ; i != -1 ; i = N[i].next){//从now节点开始访问
51 int nxt = N[i].to, f = N[i].flow;
52 if(!deep[nxt] && f > 0){//下一点未访问且流量可调整
53 deep[nxt] = deep[now] + 1;
54 q.push(nxt);
55 }
56 }
57 }
58 return deep[t];//汇点若深度为0,则无路可走
59 }
60
61 int dfs(int x, int val)//找增广路
62 {
63 if(x == t) return val;
64 int sum = 0;
65
66 for(int i = hhead[x] ; i != -1 ; i = N[i].next){
67 hhead[x] = i;
68 int nxt = N[i].to, f = N[i].flow;
69 if(f > 0 && deep[nxt] == deep[x] + 1){
70 int t = dfs(nxt ,min(val - sum, f));//
71 sum += t;
72 N[i].flow -= t;
73 N[i ^ 1].flow += t;//见107行
74 if(sum == val) break;//管道满了
75 }
76 }
77
78 if(sum == 0){//*如果sum==0,那么这个点之前没有增广路,深度清零
79 deep[x] = 0;
80 }
81 return sum;
82 }
83
84 int main(){
85
86 int T;cin >> T;
87 while(T--)
88 {
89 cin >> n;
90 memset(head, -1, sizeof(head));//读head数组,直到-1则无路,返回//因而初始化为-1
91 cnt = 0;
92
93 int cnta = 0, cntb = 0;
94 for(int i = 0 ; i < n ; i++){
95 int t, x;
96 cin >> t >> x;
97 da[i] = a[i] = x - t;
98 db[i] = b[i] = x + t;
99 }
100 //排序(方便lower_bound)+去重
101 sort(da, da + n);
102 cnta = unique(da, da + n) - da;
103 sort(db, db + n);
104 cntb = unique(db, db + n) - db;
105
106 s = cnta + cntb + 5; t = s + 1;//源点/汇点
107 //加边时成对进行,因而正反两边的编号也相邻,为了满足异或运算,编号必须从偶数开始
108 /*
109 0 ^ 1 = 1
110 1 ^ 1 = 0
111 2 ^ 1 = 3
112 3 ^ 1 = 2
113 ...
114 */
115 for(int i = 0 ; i < n ; i++){
116 int pos1 = lower_bound(da, da + cnta, a[i]) - da;
117 int pos2 = lower_bound(db, db + cntb, b[i]) - db + cnta + 2;
118 //确定V集下标
119 Add_Edge(pos1, pos2, 1);//U集与V集
120 Add_Edge(pos2, pos1, 0);
121 }
122
123 for(int i = 0 ; i < cnta ; i++){
124 Add_Edge(s, i, 1);//源点与U集
125 Add_Edge(i, s, 0);
126 }
127
128 for(int i = 0 ; i < cntb ; i++){
129 Add_Edge(i + cnta + 2, t, 1);//汇点与V集
130 Add_Edge(t, i + cnta + 2, 0);
131 }
132
133 int res = 0;
134 while(BFS(s)){//源点开搞
135 res += dfs(s, INF);
136 }
137 cout << res << endl;
138 }
139
140 return 0;
141 }