zoukankan      html  css  js  c++  java
  • BZOJ 3171 循环格 最小费用流

    题目链接:

    https://www.lydsy.com/JudgeOnline/problem.php?id=3171

    题目大意:

    一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子。每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0)。给定一个起始位置(r,c)

    ,你可以沿着箭头防线在格子间行走。即如果(r,c)是一个左箭头,那么走到(r,c-1);如果是右箭头那么走到(r,c+1);如果是上箭头那么走到(r-1,c);如果是下箭头那么走到(r+1,c);每一行和每一列都是循环的,即如果走出边界,你会出现在另一侧。
    一个完美的循环格是这样定义的:对于任意一个起始位置,你都可以i沿着箭头最终回到起始位置。如果一个循环格不满足完美,你可以随意修改任意一个元素的箭头直到完美。给定一个循环格,你需要计算最少需要修改多少个元素使其完美。

    思路:

    注意题目说明,如果出界会到另一端。

    对于每个点,拆成两个点ai bi

    对于ai,s向ai连边,容量为1,费用为0

    对于bi,bi向t连边,容量为1,费用为0

    对于每个点向四周连边,容量为1,费用取决于方向,如果是该方向那么费用为0,否则费用为1。

      1 #include<bits/stdc++.h>
      2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf
      3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时
      4 #define Min(a, b) ((a) < (b) ? (a) : (b))
      5 #define Mem(a) memset(a, 0, sizeof(a))
      6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1))
      7 #define MID(l, r) ((l) + ((r) - (l)) / 2)
      8 #define lson ((o)<<1)
      9 #define rson ((o)<<1|1)
     10 #define Accepted 0
     11 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂
     12 using namespace std;
     13 inline int read()
     14 {
     15     int x=0,f=1;char ch=getchar();
     16     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
     17     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
     18     return x*f;
     19 }
     20 
     21 typedef long long ll;
     22 const int maxn = 1000 + 10;
     23 const int MOD = 1000000007;//const引用更快,宏定义也更快
     24 const int INF = 1e9 + 7;
     25 const double eps = 1e-6;
     26 
     27 struct edge
     28 {
     29     int u, v, c, f, cost;
     30     edge(int u, int v, int c, int f, int cost):u(u), v(v), c(c), f(f), cost(cost){}
     31 };
     32 vector<edge>e;
     33 vector<int>G[maxn];
     34 int a[maxn];//找增广路每个点的水流量
     35 int p[maxn];//每次找增广路反向记录路径
     36 int d[maxn];//SPFA算法的最短路
     37 int inq[maxn];//SPFA算法是否在队列中
     38 int n, m;
     39 void init(int n)
     40 {
     41     for(int i = 0; i <= n; i++)G[i].clear();
     42     e.clear();
     43 }
     44 void addedge(int u, int v, int c, int cost)
     45 {
     46     //cout<<u<<" "<<v<<" "<<c<<" "<<cost<<endl;
     47     e.push_back(edge(u, v, c, 0, cost));
     48     e.push_back(edge(v, u, 0, 0, -cost));
     49     int m = e.size();
     50     G[u].push_back(m - 2);
     51     G[v].push_back(m - 1);
     52 }
     53 bool bellman(int s, int t, int& flow, long long & cost)
     54 {
     55     for(int i = 0; i <= n + 1; i++)d[i] = INF;//Bellman算法的初始化
     56     memset(inq, 0, sizeof(inq));
     57     d[s] = 0;inq[s] = 1;//源点s的距离设为0,标记入队
     58     p[s] = 0;a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的)
     59 
     60     queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流
     61     q.push(s);
     62     while(!q.empty())
     63     {
     64         int u = q.front();
     65         q.pop();
     66         inq[u] = 0;//入队列标记删除
     67         for(int i = 0; i < G[u].size(); i++)
     68         {
     69             edge & now = e[G[u][i]];
     70             int v = now.v;
     71             if(now.c > now.f && d[v] > d[u] + now.cost)
     72                 //now.c > now.f表示这条路还未流满(和最大流一样)
     73                 //d[v] > d[u] + e.cost Bellman 算法中边的松弛
     74             {
     75                 d[v] = d[u] + now.cost;//Bellman 算法边的松弛
     76                 p[v] = G[u][i];//反向记录边的编号
     77                 a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量
     78                 if(!inq[v]){q.push(v);inq[v] = 1;}//Bellman 算法入队
     79             }
     80         }
     81     }
     82     if(d[t] == INF)return false;//找不到增广路
     83     flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow
     84     cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用
     85     for(int u = t; u != s; u = e[p[u]].u)//逆向存边
     86     {
     87         e[p[u]].f += a[t];//正向边加上流量
     88         e[p[u] ^ 1].f -= a[t];//反向边减去流量 (和增广路算法一样)
     89     }
     90     return true;
     91 }
     92 int MincostMaxflow(int s, int t, long long & cost)
     93 {
     94     cost = 0;
     95     int flow = 0;
     96     while(bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost
     97     return flow;//返回最大流,cost引用可以直接返回最小费用
     98 }
     99 int dir[4][2] = {1,0,0,1,-1,0,0,-1};
    100 char tmp[] = "DRUL";
    101 int main()
    102 {
    103     int x, y;
    104     char Map[17][17];
    105     scanf("%d%d", &x, &y);
    106     for(int i = 0; i < x; i++)scanf("%s", Map[i]);
    107     int s = 0, t = x * y * 2 + 1;
    108     for(int i = 1; i <= x; i++)
    109     {
    110         for(int j = 1; j <= y; j++)
    111         {
    112             int ruid = y * (i - 1) + j;//拆点
    113             int chuid = ruid + x * y;
    114             addedge(s, ruid, 1, 0);
    115             addedge(chuid, t, 1, 0);
    116             for(int k = 0; k < 4; k++)
    117             {
    118                 int xx = i + dir[k][0];
    119                 int yy = j + dir[k][1];
    120                 if(xx == 0)xx = x;//出界处理
    121                 if(xx == x + 1)xx = 1;
    122                 if(yy == 0)yy = y;
    123                 if(yy == y + 1)yy = 1;
    124                 int cnt = y * (xx - 1) + yy + x * y;//到达的点的编号
    125                 if(tmp[k] == Map[i-1][j-1])//费用为0
    126                     addedge(ruid, cnt, 1, 0);
    127                 else addedge(ruid, cnt, 1, 1);
    128             }
    129         }
    130     }
    131     n = t;//点数总数为t
    132     ll ans = 0;
    133     MincostMaxflow(s, t, ans);
    134     cout<<ans<<endl;
    135     return Accepted;
    136 }
  • 相关阅读:
    [linux]无法加载so文件错误
    linux找不到.so文件的解决方法
    [Linux]core文件调试方法
    LINUX下cp f无效问题
    解决IE无法查看源文件问题
    批处理获取exe返回结果
    不得不知 云计算入门必备的60条术语
    NMS
    开启和关闭(禁用)IE8加速器功能的办法
    钩子函数
  • 原文地址:https://www.cnblogs.com/fzl194/p/9684321.html
Copyright © 2011-2022 走看看