[TJOI2013]循环格

  

题目背景

一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子。每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0)。给定一个起始位(r,c),你可以沿着箭头方向在格子间行走。即:如果(r,c)是一个左箭头,那么走到(r,c-1);如果是一个右箭头,走到(r,c+1);如果是上箭头,走到(r-1,c);如果是下箭头,走到(r+1,c)。每一行和每一列都是循环的,即如果走出边界,你会出现在另一侧。比如在一个5*5的循环格里,从(3,0)向左走会出现在(3,4)。

题目描述

一个完美的循环格是这样定义的:对于任意一个起始位置,你都可以沿着箭头最终回到起始位置。如果一个循环格不满足完美,你可以随意修改任意一个元素的箭头直到完美。例如下图,左边不是一个完美的循环格,因为只有从(1,1),(1,2),(2,0),(2,3)出发才会回到起始位置。通过修改其中两个箭头,可以得到右图,一个完美的循环格。

给定一个循环格,你需要计算最少需要修改多少个元素使其完美。

输入输出格式

输入格式:

 

第一行两个整数R和C,表示循环格的行和列。接下来R行,每一行包含C个字符LRUD表示左右上下

 

输出格式:

 

一个整数,表示最少需要修改多少个元素使得给定的循环格完美。

 

输入输出样例

输入样例#1:
4 4
RRRD
URDD
UULD
ULLL
输出样例#1:
0
输入样例#2: 
3 4
RRRD
URLL
LRRR
输出样例#2:
2

说明

数据范围

30%的数据,1 ≤ R, C ≤ 7

100%的数据,1 ≤ R, C ≤ 15

 

如果把网格中的点看成图中的点,可以发现每个点的出度都是1。

而满足条件的图肯定是若干个简单环,所以这就要求每个点的入度也是1。

然后就可以二分图匹配了,不过因为如果按照原来的方向走的话是不需要改变的,

所以还需要引入费用,跑一个最小费用最大流就行了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
#define ll long long
#define maxn 10005
#define pb push_back
using namespace std;
const int inf=1<<29;
vector<int> g[maxn]; 
struct lines{
    int from,to,flow,cap,cost;
}l[maxn*20];
int t=-1,S,T,n,m;
int d[maxn],p[maxn];
int a[maxn],ans=0;
bool iq[maxn];

inline void add(int from,int to,int cap,int cost){
    l[++t]=(lines){from,to,0,cap,cost},g[from].pb(t);
    l[++t]=(lines){to,from,0,0,-cost},g[to].pb(t);
}

inline bool BFS(){
    queue<int> q;
    memset(d,0x3f,sizeof(d));
    p[S]=0,a[S]=inf,iq[S]=1;
    d[S]=0,q.push(S);
    
    int x; lines e;
    while(!q.empty()){
        x=q.front(),q.pop();
        
        for(int i=g[x].size()-1;i>=0;i--){
            e=l[g[x][i]];
            if(e.flow<e.cap&&d[x]+e.cost<d[e.to]){
                d[e.to]=d[x]+e.cost;
                p[e.to]=g[x][i];
                a[e.to]=min(a[x],e.cap-e.flow);
                if(!iq[e.to]) iq[e.to]=1,q.push(e.to); 
            }
        }
        
        iq[x]=0;
    }
    
    if(d[T]==d[T+1]) return 0;
    
    ans+=a[T]*d[T];
    
    int now=T,pre;
    while(now!=S){
        pre=p[now];
        l[pre].flow+=a[T];
        l[pre^1].flow-=a[T];
        now=l[pre].from;
    }
    
    return 1;
}

inline void MFMC(){
    while(BFS());
}

//上1下2左3右4 
int id[25][25],num;
int val[maxn][6],dy[666];
char ch;

int main(){
    dy['L']=3,dy['R']=4;
    dy['U']=1,dy['D']=2;
    
    scanf("%d%d",&n,&m),S=0,T=2*n*m+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            id[i][j]=++num;
            ch=getchar();
            while(!dy[ch]) ch=getchar();
            val[num][dy[ch]]=1;
        }
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            int now=id[i][j];
            if(i==1) add(now,id[n][j]+num,1,val[now][1]^1);
            else add(now,id[i-1][j]+num,1,val[now][1]^1);
            
            if(i==n) add(now,id[1][j]+num,1,val[now][2]^1);
            else add(now,id[i+1][j]+num,1,val[now][2]^1);
            
            if(j==1) add(now,id[i][m]+num,1,val[now][3]^1);
            else add(now,id[i][j-1]+num,1,val[now][3]^1);
            
            if(j==m) add(now,id[i][1]+num,1,val[now][4]^1);
            else add(now,id[i][j+1]+num,1,val[now][4]^1);
        }
    
    
    for(int i=1;i<=num;i++){
        add(S,i,1,0);
        add(i+num,T,1,0);
    }
    
    MFMC();
    printf("%d\n",ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/JYYHH/p/8358214.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值