• 「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(一脸懵逼

  • 相关阅读:
    docker容器的IPV6处理。
    PVE添加旧磁盘,重装系统,数据还在
    chrome多开
    MySQL 学习笔记(一)MySQL 事务的ACID特性
    kafka丢消息的情况及处理
    【TS基础】类型“Window & typeof globalThis”上不存在属性“$loading”
    vue 3 的复制功能 vueclipboard3
    前端 判断页面进的是pc还是移动端
    微信浏览器唤起微信支付
    vue中配置可选链操作符兼容
  • 原文地址:https://www.cnblogs.com/cminus/p/6879896.html
Copyright © 2020-2023  润新知