zoukankan      html  css  js  c++  java
  • [2017清华集训]无限之环

    嘟嘟嘟


    这算是我做过的最神的一道网络流题了。
    话说有人能想到这题用费用流吗


    某神仙说,看到网格图,就能想到黑白染色(我就想不到)。那么假设我们现在想到了。
    然后用1个流量代表一个管道接口,那么存在解得条件必定是总流量等于接口数除以2。


    先想一下不能转动的情况:容易想到拆点,把一个点分别拆成这个点的上、右、下、左接口四个点。然后先把整个图连通,即((i, j))的右接口连((i, j + 1))的左接口,((i, j))的下接口连((i + 1, j))的上接口。注意,因为是黑白染色,源点连向黑点,白点连向汇点,所以如果连边的时候出现白点连黑点,要反过来。
    然后对于每一个黑点,从源点向他的每一个存在的接口连一条容量为1,费用为0的边;对于每一个白点,其当前存在的接口向汇点连一条((1, 0))的边。
    这样这个不能转动的图就构造完了。


    那么每一个点能转动该怎么办?连边的确很巧妙。
    通过观察发现,能转动的管道无非三种:


    这种情况是最简单的。
    向右转90度,那么就从他的上接口向右接口连一条((1, 1))的边;向左转90度,就从上接口向左接口连一条((1, 1))的边;转180度,就从上接口向下接口连一条((1, 2))的边。


    那么这种情况也同理。向右转90度,发现向右的接口还在,那么只用从上接口向下接口连一条((1, 1))的边;向左转90度,就从右接口向左接口连一条((1, 1))的边;180度不用再连边了,因为上述两种情况合起来就是转180度!


    后一种情况想想也就出来了:从左、有接口向下接口连一条((1, 1))的边,从上接口向下接口连一条((1, 2))的边。

    然后这题就是这么回事了!
    不过建图代码不是那么好些,因为每一种情况自身还会旋转,所以维护一个变量turn,表示他已经转了几次,这样向右向左向下转其实就是turn+1或是turn+2了。于是我们就成功避免了16种情况的大分类讨论。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define In inline
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxN = 1e4 + 5;
    const int maxe = 4e6 + 5;
    inline ll read()
    {
      ll ans = 0;
      char ch = getchar(), last = ' ';
      while(!isdigit(ch)) last = ch, ch = getchar();
      while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
      if(last == '-') ans = -ans;
      return ans;
    }
    inline void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    
    int n, m, s, t, TOT = 0;
    
    struct Edge
    {
      int nxt, from, to, cap, cos;
    }e[maxe];
    int head[maxN], ecnt = -1;
    In void addEdge(int x, int y, int w, int c, int flg)  //black to white
    {
      if(y != t && flg) swap(x, y);  //if x is white, swap x and y
      e[++ecnt] = (Edge){head[x], x, y, w, c};
      head[x] = ecnt;
      e[++ecnt] = (Edge){head[y], y, x, 0, -c};
      head[y] = ecnt;
    }
    
    In int num(int x, int y, int z)
    {
      return ((m * (x - 1) + y - 1) << 2) + z + 1;
    }
    #define U(x, y) num(x, y, 0)
    #define R(x, y) num(x, y, 1)
    #define D(x, y) num(x, y, 2)
    #define L(x, y) num(x, y, 3)
    
    In void build(int x, int y, int z, int flg)
    {
      int tp = 0;
      for(int i = 0; i < 4; ++i)  //to start/end
        if((z >> i) & 1)
          {
    	++TOT; ++tp;
    	if((x + y) & 1) addEdge(num(x, y, i), t, 1, 0, flg);
    	else addEdge(s, num(x, y, i), 1, 0, flg);
          }
      int turn = 0;
      if(tp == 1)
        {
          if(z == 2) turn = 1;
          if(z == 4) turn = 2;
          if(z == 8) turn = 3;
          int u = num(x, y, turn);
          addEdge(u, num(x, y, (turn + 3) % 4), 1, 1, flg);
          addEdge(u, num(x, y, (turn + 1) % 4), 1, 1, flg);
          addEdge(u, num(x, y, (turn + 2) % 4), 1, 2, flg);
        }
      if(tp == 2)
        {
          if(z == 6) turn = 1;
          if(z == 12) turn = 2;
          if(z == 9) turn = 3;
          if(z != 5 && z != 10)
    	{
    	  int u = num(x, y, turn), v = num(x, y, (turn + 1) % 4);
    	  addEdge(u, num(x, y, (turn + 2) % 4), 1, 1, flg);
    	  addEdge(v, num(x, y, (turn + 3) % 4), 1, 1, flg);
    	}
        }
      if(tp == 3)
        {
          if(z == 7) turn = 1;
          if(z == 14) turn = 2;
          if(z == 13) turn = 3;
          int v = num(x, y, (turn + 2) % 4);
          addEdge(num(x, y, turn), v, 1, 2, flg);
          addEdge(num(x, y, (turn + 1) % 4), v, 1, 1, flg);
          addEdge(num(x, y, (turn + 3) % 4), v, 1, 1, flg);
        }
    }
    
    bool in[maxN];
    int dis[maxN], pre[maxN], flow[maxN];
    In bool spfa()
    {
      Mem(dis, 0x3f); Mem(in, 0);
      queue<int> q; q.push(s);
      in[s] = 1; dis[s] = 0; flow[s] = INF;
      while(!q.empty())
        {
          int now = q.front(); q.pop(); in[now] = 0;
          for(int i = head[now], v; ~i; i = e[i].nxt)
    	{
    	  v = e[i].to;
    	  if(e[i].cap && dis[now] + e[i].cos < dis[v])
    	    {
    	      dis[v] = dis[now] + e[i].cos;
    	      pre[v] = i;
    	      flow[v] = min(flow[now], e[i].cap);
    	      if(!in[v]) in[v] = 1, q.push(v);
    	    }
    	}
        }
      return dis[t] ^ INF;
    }
    int maxFlow = 0, minCost = 0;
    In void update()
    {
      int x = t;
      while(x ^ s)
        {
          int i = pre[x];
          e[i].cap -= flow[t];
          e[i ^ 1].cap += flow[t];
          x = e[i].from;
        }
      maxFlow += flow[t];
      minCost += flow[t] * dis[t];
    }
    In void MCMF()
    {
      while(spfa()) update();
    }
    
    int main()
    {
      Mem(head, -1);
      n = read(), m = read();
      s = 0, t = n * m * 4 + 1;
      for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
          {
    	int x = read();
    	build(i, j, x, (i + j) & 1);
          }
      for(int i = 1; i <= n; ++i)  //相邻接口连边
        for(int j = 1; j <= m; ++j)
          {
    	if(j < m) addEdge(R(i, j), L(i, j + 1), 1, 0, (i + j) & 1);
    	if(i < n) addEdge(D(i, j), U(i + 1, j), 1, 0, (i + j) & 1);
          }
      MCMF();
      write(maxFlow == (TOT >> 1) ? minCost : -1), enter;
      return 0;
    }
    
  • 相关阅读:
    Windows 科研软件推荐
    有关Python 包 (package) 的基本知识
    《Using Python to Access Web Data》Week4 Programs that Surf the Web 课堂笔记
    Coursera助学金申请模板
    《Using Databases with Python》 Week2 Basic Structured Query Language 课堂笔记
    Jupyter 解决单个变量输出问题
    解决 pandas 中打印 DataFrame 行列显示不全的问题
    《Using Python to Access Web Data》 Week3 Networks and Sockets 课堂笔记
    缓存击穿及解决方案
    jvm垃圾收集器
  • 原文地址:https://www.cnblogs.com/mrclr/p/10525969.html
Copyright © 2011-2022 走看看