首页 > 代码库 > 「CODVES 1922 」骑士共存问题(二分图的最大独立集|网络流)&dinic

「CODVES 1922 」骑士共存问题(二分图的最大独立集|网络流)&dinic

首先是题目链接  http://codevs.cn/problem/1922/

结果发现题目没图(心情复杂

然后去网上扒了一张图

技术分享

大概就是这样了。

如果把每个点和它可以攻击的点连一条边,那问题就变成了求二分图的最大独立集了 (二分图最大独立集:即一个点集,集合中任两个结点不相邻),然后就是建图了。

题图非常好心的帮忙染色了,所以我们可以看出来,一个点可以到达的点和它的颜色是不一样的,所以只需要黑白染色就可以了,然后把黑点看作一个集合, 白点看作一个集合,又因为二分图最大独立集= 二分图最大匹配,而后我们就只需要求最大匹配就可以了。

最大匹配最经典的就是匈牙利啦,但是网络流也可以做, 而且dinic在边容量为一的时候跑得特别快,所以就用dinic了(顺便练习一下dinic233

建图方法:超级源S连接所有的黑点,且边的容量为1, 黑点连接它所有能攻击到的白点,边的容量为INF, 然后所有的白点再连接超级汇T, 容量仍然是为1。跑一遍dinic就可以了(建图的正确性可以自己画图证明

  1 #include <cstdio>
  2 #include <vector>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <queue>
  6 #include <iostream>
  7 using namespace std;
  8 const int N = 210;
  9 
 10 int n, m, cnt, w;
 11 
 12 struct Edge{
 13     int from, to, cap, flow;
 14 };
 15 vector < int > G[N*N]; 
 16 vector < Edge > edge;
 17 bool vis[N*N];
 18 int s, t, d[N*N], cur[N*N], INF = (1<<30), flag[N][N];
 19 int py[8][2] = {{2, 1}, {2, -1}, {-2, 1}, {-2, -1}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
 20 
 21 inline void read(int &ans){
 22     static char buf = getchar();
 23     register int neg = 1;
 24     ans = 0;
 25     for (;!isdigit(buf); buf = getchar())
 26         if (buf == -)  neg = -1;
 27     for (;isdigit(buf); buf = getchar())
 28         ans = ans*10 + buf - 0;
 29     ans *= neg;
 30 }
 31 
 32 inline void Add(int from, int to, int cap){
 33     edge.push_back((Edge){from, to, cap, 0});
 34     edge.push_back((Edge){to, from, 0, 0});
 35     int m = edge.size();
 36     G[from].push_back(m - 2);
 37     G[to].push_back(m - 1);
 38 }
 39 
 40 bool ok(int x, int y){
 41     if (x > 0 && x <= n && y > 0 && y <= n)
 42         if (flag[x][y] != -1)
 43             return true;
 44     return false;
 45 }
 46 
 47 inline void build(){
 48     for (int i = 1; i <= n; i++)
 49         for (int j = 1; j <= n; j++)
 50             if (flag[i][j] == -1)    continue;
 51             else if (flag[i][j] < cnt){
 52                 for (int k = 0; k < 8; k++){
 53                     int x = i + py[k][0];
 54                     int y = j + py[k][1];
 55                     if (ok(x, y))
 56                         Add(flag[i][j], flag[x][y], INF);
 57                 }
 58                 Add(0, flag[i][j], 1);
 59             }
 60             else Add(flag[i][j], w, 1);
 61 }
 62 
 63 inline bool BFS(){
 64     memset(vis, 0, sizeof(vis));
 65     memset(d, 0xff, sizeof(d));
 66     queue < int > q;
 67     q.push(s);
 68     d[s] = 0;
 69     vis[s] = 1;
 70     while(!q.empty()){
 71         int u = q.front(); q.pop();
 72         for (int i = 0; i < G[u].size(); i++){
 73             Edge& e = edge[G[u][i]];
 74             if (!vis[e.to] && e.cap > e.flow){
 75                 d[e.to] = d[u] + 1;
 76                 vis[e.to] = 1;
 77                 q.push(e.to);
 78             }
 79         }
 80     }
 81     return vis[t];
 82 }
 83 
 84 int dfs(int x, int a){
 85     if (x == t || a == 0)    return a;
 86     int flow = 0, f;
 87     for (int& i = cur[x]; i < G[x].size(); i++){
 88         Edge& e = edge[G[x][i]];
 89         if (d[x] + 1 == d[e.to] && (f = dfs(e.to, min(e.cap - e.flow, a))) > 0){
 90             e.flow += f;
 91             edge[G[x][i]^1].flow -= f;
 92             flow += f;
 93             a -= f;
 94             if (a == 0)    break;
 95         }
 96     }
 97     return flow;
 98 }
 99 
100 inline int dinic(){
101     int ans = 0;
102     while(BFS()){
103         memset(cur, 0, sizeof(cur));
104         ans += dfs(0, INF);
105     }
106     return ans;
107 }
108 
109 int main(){
110     read(n); read(m);
111     for (int i = 0; i < m; i++){
112         int x, y;
113         read(x); read(y);
114         flag[x][y] = -1;
115     }
116     cnt = 1;
117     w = (n * n + 1)/2 + 1;
118     for (int i = 1; i <= n; i++)
119         for (int j = 1; j <= n; j++)
120             if ((i + j)%2 == 0){
121                 if (flag[i][j] != -1) flag[i][j] = cnt;
122                 cnt++;
123             }
124             else{
125                 if (flag[i][j] != -1)  flag[i][j] = w;
126                 w++;
127             }
128     s = 0, t = n*n + 1;
129     build();
130     printf("%d", n*n - m - dinic());
131     return 0;
132 }

然而这道题最开始写的时候TLE了

然后就加了当前弧优化

然后就A了(滑稽

但是可以看得出来非常的慢

测试点#kni0.in 结果: 内存使用量: 1512kB 时间使用量: 1ms 
测试点#kni1.in 结果: 内存使用量: 1388kB 时间使用量: 1ms 
测试点#kni10.in 结果: 内存使用量: 7256kB 时间使用量: 415ms 
测试点#kni2.in 结果: 内存使用量: 1516kB 时间使用量: 1ms 
测试点#kni3.in 结果: 内存使用量: 1644kB 时间使用量: 1ms 
测试点#kni4.in 结果: 内存使用量: 1772kB 时间使用量: 3ms 
测试点#kni5.in 结果: 内存使用量: 1772kB 时间使用量: 4ms 
测试点#kni6.in 结果: 内存使用量: 1768kB 时间使用量: 2ms 
测试点#kni7.in 结果: 内存使用量: 2020kB 时间使用量: 4ms 
测试点#kni8.in 结果: 内存使用量: 4444kB 时间使用量: 143ms 
测试点#kni9.in 结果: 内存使用量: 11732kB 时间使用量: 927ms 

然后上网搜了一下其他的题解,发现其他的代码都跑得很快,然后就把建图方式改了一下

  1 #include <cstdio>
  2 #include <vector>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <queue>
  6 #include <iostream>
  7 using namespace std;
  8 const int N = 210;
  9 
 10 int n, m, cnt, w;
 11 
 12 struct Edge{
 13     int from, to, cap, flow;
 14 };
 15 vector < int > G[N*N]; 
 16 vector < Edge > edge;
 17 bool vis[N*N];
 18 int s, t, d[N*N], cur[N*N], INF = (1<<30), flag[N][N], b[N*N];
 19 int py[8][2] = {{2, 1}, {2, -1}, {-2, 1}, {-2, -1}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
 20 
 21 inline void read(int &ans){
 22     static char buf = getchar();
 23     register int neg = 1;
 24     ans = 0;
 25     for (;!isdigit(buf); buf = getchar())
 26         if (buf == -)  neg = -1;
 27     for (;isdigit(buf); buf = getchar())
 28         ans = ans*10 + buf - 0;
 29     ans *= neg;
 30 }
 31 
 32 inline void Add(int from, int to, int cap){
 33     edge.push_back((Edge){from, to, cap, 0});
 34     edge.push_back((Edge){to, from, 0, 0});
 35     int m = edge.size();
 36     G[from].push_back(m - 2);
 37     G[to].push_back(m - 1);
 38 }
 39 
 40 bool ok(int x, int y){
 41     if (x > 0 && x <= n && y > 0 && y <= n)
 42         if (flag[x][y] != -1)
 43             return true;
 44     return false;
 45 }
 46 
 47 inline int hash(int i,int j){
 48     return (i - 1)*n + j;
 49 }
 50 
 51 inline bool BFS(){
 52     memset(vis, 0, sizeof(vis));
 53     memset(d, 0xff, sizeof(d));
 54     queue < int > q;
 55     q.push(s);
 56     d[s] = 0;
 57     vis[s] = 1;
 58     while(!q.empty()){
 59         int u = q.front(); q.pop();
 60         for (int i = 0; i < G[u].size(); i++){
 61             Edge& e = edge[G[u][i]];
 62             if (!vis[e.to] && e.cap > e.flow){
 63                 d[e.to] = d[u] + 1;
 64                 vis[e.to] = 1;
 65                 q.push(e.to);
 66             }
 67         }
 68     }
 69     return vis[t];
 70 }
 71 
 72 int dfs(int x, int a){
 73     if (x == t || a == 0)    return a;
 74     int flow = 0, f;
 75     for (int& i = cur[x]; i < G[x].size(); i++){
 76         Edge& e = edge[G[x][i]];
 77         if (d[x] + 1 == d[e.to] && (f = dfs(e.to, min(e.cap - e.flow, a))) > 0){
 78             e.flow += f;
 79             edge[G[x][i]^1].flow -= f;
 80             flow += f;
 81             a -= f;
 82             if (a == 0)    break;
 83         }
 84     }
 85     return flow;
 86 }
 87 
 88 inline int dinic(){
 89     int ans = 0;
 90     while(BFS()){
 91         memset(cur, 0, sizeof(cur));
 92         ans += dfs(0, INF);
 93     }
 94     return ans;
 95 }
 96 
 97 int main(){
 98     read(n); read(m);
 99     for (int i = 0; i < m; i++){
100         int x, y;
101         read(x); read(y);
102         b[hash(x, y)] = 1;
103     }
104     cnt = 1;
105     s = 0, t = n*n + 1;
106     for(int i = 1; i <= n;i++)
107             for(int j = 1; j <=n; j++){
108                 if(!b[hash(i,j)] && ((i+j)&1)){
109                     Add(s, hash(i,j), 1);
110                     if(i > 2 && j > 1 && !b[hash(i-2,j-1)])       
111                         Add(hash(i,j), hash(i-2,j-1), INF);
112                     if(i > 2 && j + 1 <= n && !b[hash(i-2,j+1)])  
113                         Add(hash(i,j), hash(i-2,j+1), INF);
114                     if(i > 1 && j > 2 && !b[hash(i-1,j-2)])       
115                         Add(hash(i,j), hash(i-1,j-2), INF);
116                     if(i > 1 && j + 2 <= n &&!b[hash(i-1,j+2)])   
117                         Add(hash(i,j), hash(i-1,j+2), INF);
118                     if(i + 2 <= n && j > 1 && !b[hash(i+2,j-1)])  
119                         Add(hash(i,j), hash(i+2,j-1), INF);
120                     if(i + 2 <= n && j + 1 <= n && !b[hash(i+2,j+1)]) 
121                         Add(hash(i,j), hash(i+2,j+1), INF);
122                     if(i + 1 <= n && j > 2 && !b[hash(i+1,j-2)])  
123                         Add(hash(i,j), hash(i+1,j-2), INF);
124                     if(i + 1 <= n && j + 2 <= n && !b[hash(i+1,j+2)]) 
125                         Add(hash(i,j), hash(i+1,j+2), INF);
126                 }
127                 if(!b[hash(i,j)] && !((i+j)&1))   Add(hash(i,j), t, 1);
128             }
129 
130     printf("%d", n*n - m - dinic());
131     return 0;
132 
133 }

修改之后

测试点#kni0.in 结果: 内存使用量: 1640kB 时间使用量: 2ms 
测试点#kni1.in 结果: 内存使用量: 1512kB 时间使用量: 2ms 
测试点#kni10.in 结果: 内存使用量: 7256kB 时间使用量: 178ms 
测试点#kni2.in 结果: 内存使用量: 1512kB 时间使用量: 2ms 
测试点#kni3.in 结果: 内存使用量: 1640kB 时间使用量: 2ms 
测试点#kni4.in 结果: 内存使用量: 1772kB 时间使用量: 2ms 
测试点#kni5.in 结果: 内存使用量: 1772kB 时间使用量: 2ms 
测试点#kni6.in 结果: 内存使用量: 1772kB 时间使用量: 2ms 
测试点#kni7.in 结果: 内存使用量: 2020kB 时间使用量: 4ms 
测试点#kni8.in 结果: 内存使用量: 4568kB 时间使用量: 92ms 
测试点#kni9.in 结果: 内存使用量: 11608kB 时间使用量: 52ms 

少了一个二维循环 + 减少了常数

结果快了1200ms(一脸懵逼

 

「CODVES 1922 」骑士共存问题(二分图的最大独立集|网络流)&dinic