BZOJ-3171-循环格-TJOI2013-费用流

描述

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

,你可以沿着箭头防线在格子间行走。即如果(r,c)是一个左箭头,那么走到(r,c-1);如果是右箭头那么走到(r,c+1);如果是上箭头那么走到(r-1,c);如果是下箭头那么走到(r+1,c);每一行和每一列都是循环的,即如果走出边界,你会出现在另一侧。

一个完美的循环格是这样定义的:对于任意一个起始位置,你都可以i沿着箭头最终回到起始位置。如果一个循环格不满足完美,你可以随意修改任意一个元素的箭头直到完美。给定一个循环格,你需要计算最少需要修改多少个元素使其完美。


分析

  • 和星际竞速那道题有相通之处. 当时做星际竞速时总结的建模方法在这里用到了.
  • 凡是遇到使每个点都经过一次的题目就可以考虑拆点建图. 拆点, S向Xi连一条容量为1费用为0的边, 表示从X出发. Yi向T连一条容量为1费用为0的边, 表示到达Y.
  • 格子X的Xi向原方向指向的格子Y的Yj连一条容量为INF, 费用为0的边, 向另外三个方向指向的格子连一条容量为INF, 费用为1的边. 表示修改.

代码

#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
          using namespace std; const int INF = 1000000000; const int maxn = 2*15*15 + 10; struct Edge { int from, to, cap, flow, cost; }; struct MCMF { int n, m, s, t; vector 
         
           edges; vector 
          
            G[maxn]; int inq[maxn], d[maxn], p[maxn], a[maxn]; void init(int n, int s, int t) { this->n = n; this->s = s; this->t = t; } void AddEdge(int from, int to, int cap, int cost) { edges.push_back((Edge){from, to, cap, 0, cost}); edges.push_back((Edge){to, from, 0, 0, -cost}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BellmanFord(int& cost) { for(int i = 0; i < n; i++) d[i] = INF; memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; queue 
           
             Q; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = 0; for(int i = 0; i < G[u].size(); i++) { Edge& e = edges[G[u][i]]; if(e.cap > e.flow && d[e.to] > d[u] + e.cost) { d[e.to] = d[u] + e.cost; p[e.to] = G[u][i]; a[e.to] = min(a[u], e.cap - e.flow); if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } } } } if(d[t] == INF) return false; cost += d[t] * a[t]; int u = t; while(u != s) { edges[p[u]].flow += a[t]; edges[p[u]^1].flow -= a[t]; u = edges[p[u]].from; } return true; } int Mincost() { int cost = 0; while(BellmanFord(cost)); return cost; } }g; int id[maxn][maxn]; int main() { int n, m, s, t; scanf("%d %d", &n, &m); int c = 0; for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) id[i][j] = ++c; g.init(c+c+2, s=0, t=c+c+1); for(int i = 0; i < n; i++) { char str[20]; scanf("%s", str); for(int j = 0; j < m; j++) { g.AddEdge(s, id[i][j], 1, 0); g.AddEdge(id[i][j]+c, t, 1, 0); switch(str[j]) { case 'U': g.AddEdge(id[i][j], id[(i+n-1)%n][j]+c, INF, 0); g.AddEdge(id[i][j], id[(i+1)%n][j]+c, INF, 1); g.AddEdge(id[i][j], id[i][(j+m-1)%m]+c, INF, 1); g.AddEdge(id[i][j], id[i][(j+1)%m]+c, INF, 1); break; case 'D': g.AddEdge(id[i][j], id[(i+n-1)%n][j]+c, INF, 1); g.AddEdge(id[i][j], id[(i+1)%n][j]+c, INF, 0); g.AddEdge(id[i][j], id[i][(j+m-1)%m]+c, INF, 1); g.AddEdge(id[i][j], id[i][(j+1)%m]+c, INF, 1); break; case 'L': g.AddEdge(id[i][j], id[(i+n-1)%n][j]+c, INF, 1); g.AddEdge(id[i][j], id[(i+1)%n][j]+c, INF, 1); g.AddEdge(id[i][j], id[i][(j+m-1)%m]+c, INF, 0); g.AddEdge(id[i][j], id[i][(j+1)%m]+c, INF, 1); break; case 'R': g.AddEdge(id[i][j], id[(i+n-1)%n][j]+c, INF, 1); g.AddEdge(id[i][j], id[(i+1)%n][j]+c, INF, 1); g.AddEdge(id[i][j], id[i][(j+m-1)%m]+c, INF, 1); g.AddEdge(id[i][j], id[i][(j+1)%m]+c, INF, 0); break; } } } printf("%d\n", g.Mincost()); return 0; } 
            
           
          
        
       
       
      
      
     
     
    
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值