「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 结果:AC 内存使用量: 1512kB 时间使用量: 1ms 
测试点#kni1.in 结果:AC 内存使用量: 1388kB 时间使用量: 1ms 
测试点#kni10.in 结果:AC 内存使用量: 7256kB 时间使用量: 415ms 
测试点#kni2.in 结果:AC 内存使用量: 1516kB 时间使用量: 1ms 
测试点#kni3.in 结果:AC 内存使用量: 1644kB 时间使用量: 1ms 
测试点#kni4.in 结果:AC 内存使用量: 1772kB 时间使用量: 3ms 
测试点#kni5.in 结果:AC 内存使用量: 1772kB 时间使用量: 4ms 
测试点#kni6.in 结果:AC 内存使用量: 1768kB 时间使用量: 2ms 
测试点#kni7.in 结果:AC 内存使用量: 2020kB 时间使用量: 4ms 
测试点#kni8.in 结果:AC 内存使用量: 4444kB 时间使用量: 143ms 
测试点#kni9.in 结果:AC 内存使用量: 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 结果:AC 内存使用量: 1640kB 时间使用量: 2ms 
测试点#kni1.in 结果:AC 内存使用量: 1512kB 时间使用量: 2ms 
测试点#kni10.in 结果:AC 内存使用量: 7256kB 时间使用量: 178ms 
测试点#kni2.in 结果:AC 内存使用量: 1512kB 时间使用量: 2ms 
测试点#kni3.in 结果:AC 内存使用量: 1640kB 时间使用量: 2ms 
测试点#kni4.in 结果:AC 内存使用量: 1772kB 时间使用量: 2ms 
测试点#kni5.in 结果:AC 内存使用量: 1772kB 时间使用量: 2ms 
测试点#kni6.in 结果:AC 内存使用量: 1772kB 时间使用量: 2ms 
测试点#kni7.in 结果:AC 内存使用量: 2020kB 时间使用量: 4ms 
测试点#kni8.in 结果:AC 内存使用量: 4568kB 时间使用量: 92ms 
测试点#kni9.in 结果:AC 内存使用量: 11608kB 时间使用量: 52ms

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

结果快了1200ms(一脸懵逼

时间: 2024-12-12 12:07:12

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

「AHOI2014/JSOI2014」骑士游戏

「AHOI2014/JSOI2014」骑士游戏 传送门 考虑 \(\text{DP}\). 设 \(dp_i\) 表示灭种(雾)一只编号为 \(i\) 的怪物的代价. 那么转移显然是: \[dp_i = \min(K_i, S_i + \sum_{j = 1}^{R_i} dp_{v_j})\] 但是我们会发现这个东西是有后效性的... 所以我们会想要用建图然后跑一个最短路什么的来搞... 于是我们观察到上面那个 \(\text{DP}\) 式子中,\(dp_i\) 如果用后面那一项来转移,显然

骑士共存问题(二分图最大独立集)

//http://www.cnblogs.com/IMGavin/ #include <iostream> #include <stdio.h> #include <cstdlib> #include <cstring> #include <queue> #include <vector> #include <map> #include <stack> #include <set> #include

【wikioi】1922 骑士共存问题(网络流/二分图匹配)

用匈牙利tle啊喂?和网络流不都是n^3的吗.... (更新:what!!!!!!发现个无语的问题,.!!!!结构比数组快啊orz,这节奏不对啊....以后图都写结构的节奏啊... #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <algorithm> using namespace

【CODEVS】1922 骑士共存问题

[算法]二分图最大匹配(最大流) [题解]按(i+j)奇偶性染色后,发现棋子跳到的地方刚好异色. 然后就是二分图了,对于每个奇点向可以跳到的地方连边,偶点不需连(可逆). 所以题目要求转换为求二分图上最大独立集(对于每条边,至少有一个点不被选中). 最大独立集=总点数-最小割 //代码略 //hzwer's code: #include<iostream> #include<cstdio> #include<cstring> #define INF 0x7fffffff

[网络流24题] 骑士共存

746. [网络流24题] 骑士共存 ★★☆   输入文件:knight.in   输出文件:knight.out   简单对比 时间限制:1 s   内存限制:128 MB 骑士共存问题 «问题描述: 在一个n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示.棋盘 上某些方格设置了障碍,骑士不得进入. «编程任务: 对于给定的n*n个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑 士,使得它们彼此互不攻击. «数据输入: 由文件knight.in给出输入数据.第一

[网络流24题] 骑士共存(cogs 746)

骑士共存问题?问题描述:在一个n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示.棋盘 上某些方格设置了障碍,骑士不得进入. ?编程任务:对于给定的n*n个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑士,使得它们彼此互不攻击.?数据输入:由文件knight.in给出输入数据.第一行有2 个正整数n 和m (1<=n<=200, 0<=m<=n*n)<n2),< span="">分别表示棋盘的大小和障碍数.接下来的

P3355 骑士共存问题 二分建图 + 当前弧优化dinic

P3355 骑士共存问题 题意: 也是一个棋盘,规则是“马”不能相互打到. 思路: 奇偶点分开,二分图建图,这道题要注意每个点可以跑八个方向,两边都可以跑,所以边 = 20 * n * n. 然后dinic 要用当前弧优化. #include <algorithm> #include <iterator> #include <iostream> #include <cstring> #include <cstdlib> #include <

怎样将「插件化」接入到项目之中?

本期移动开发精英社群讨论的主题是「插件化」,上网查了一下,发现一篇 CSDN 博主写的文章<Android 使用动态载入框架DL进行插件化开发>.此处引用原作者的话: 随着应用的不断迭代,应用的体积不断增大,项目越来越臃肿,冗余添加.项目新功能的加入,无法确定与用户匹配性,发生严重异常往往牵一发而动全身,仅仅能紧急公布补丁版本号,强制用户进行更新.结果频繁的更新.反而easy减少用户使用黏性,或者是公司业务的不断发展,同系的应用越来越多,传统方式须要通过用户量最大的主项目进行引导下载并安装.

[COGS746] [网络流24题] 骑士共存

★★☆   输入文件:knight.in   输出文件:knight.out   简单对比 时间限制:1 s   内存限制:128 MB 骑士共存问题 «问题描述: 在一个n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示.棋盘 上某些方格设置了障碍,骑士不得进入. «编程任务: 对于给定的n*n个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑 士,使得它们彼此互不攻击. «数据输入: 由文件knight.in给出输入数据.第一行有2 个正整数n 和m (1<=n<