bzoj3171 [Tjoi2013]循环格

Description

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

Input

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

Output

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

Sample Input
3 4
RRRD
URLL
LRRR
Sample Output
2

HINT
1<=R,L<=15

分析:
很明显,图中只能有若干个环
这就像一个欧拉回路一样,
每个点的出度一定等于入度
然而每个点的出度都是一,那也就是说入度一定是一
这样就变成了怎么改变方向,使每个格子的出度和入度都等于一

这就让我想起了有一道混合图的网络流题
那道题就是让我们给每一条无向边定向,
使得图成为一个欧拉路
每改变一个格子的方向就会使两个格子的入度更改

我一开始想的很简单:
把每个点拆成两个点
称作1部,2部
如果x—>y
我们就连接x1—>y2
跑一个最大匹配,
最后答案是点数-最大匹配
结果发现连样例都过不了

实际上这是一道最小费用最大流
还是拆点,每个点向相邻的格子连边,
如果是箭头所指的方向,那么费用是0
否则费用为1
跑一遍费用流就可以了

tip

我一开始的理解是只能在格子内奔跑,
如果跑了出去是不合法的,
后来才明白,题目的意思是循环奔跑,跑出去了之后再从头开始

#include<cstdio>
#include<iostream>
#include<cstring>

using namespace std;

const int INF=0x33333333;
const int N=40001;
struct node{
	int x,y,v,c,nxt;
};
node way[N<<2];
int st[N],pre[N],tot=-1,dis[N],q[N],tou,wei,n,m,deep[N],cur[N],s,t;
bool p[N];

int get(int x,int y){return (x-1)*m+y;}

void add(int u,int w,int v,int cc)
{
	tot++;
	way[tot].x=u;way[tot].y=w;way[tot].c=cc;way[tot].v=v;way[tot].nxt=st[u];st[u]=tot;
	tot++;
	way[tot].x=w;way[tot].y=u;way[tot].c=-cc;way[tot].v=0;way[tot].nxt=st[w];st[w]=tot;
}

int spfa(int s,int t)
{
	memset(dis,0x33,sizeof(dis));
	memset(pre,-1,sizeof(pre));
	memset(p,1,sizeof(p));
	tou=wei=0;
	q[++wei]=s;
	p[s]=0; dis[s]=0;
	do
	{
		int r=q[++tou];
		for (int i=st[r];i!=-1;i=way[i].nxt)
		    if (way[i].v&&dis[r]+way[i].c<dis[way[i].y])
		    {
		    	dis[way[i].y]=dis[r]+way[i].c;
		    	pre[way[i].y]=i;
		    	if (p[way[i].y])
		    	{
		    		p[way[i].y]=0;
		    		q[++wei]=way[i].y;
				}
			}
		p[r]=1;
	}
	while (tou<wei);
	return dis[t]!=INF;
}

void doit()
{
	int ans=0;
	while (spfa(s,t))
	{
		int sum=INF;
		for (int i=t;i!=s;i=way[pre[i]].x)
		    sum=min(sum,way[pre[i]].v);
		ans+=sum*dis[t];
		for (int i=t;i!=s;i=way[pre[i]].x)
		    way[pre[i]].v-=sum,
		    way[pre[i]^1].v+=sum;
	}
	printf("%d",ans);
}

int main()
{
	memset(st,-1,sizeof(st));
	scanf("%d%d",&n,&m);
	s=0; t=n*m*2+1;
	char ch[20];
	for (int i=1;i<=n;i++)
	{
		scanf("%s",&ch);
		for (int j=1;j<=m;j++)
		{
			int now=get(i,j);
			add(s,now,1,0);
			add(now+n*m,t,1,0);
			int xx,yy;
			if (ch[j-1]=='U'){
				xx=(i-1==0 ? n:i-1); add(now,get(xx,j)+n*m,1,0);
				yy=(j-1==0 ? m:j-1); add(now,get(i,yy)+n*m,1,1);
				xx=(i+1>n ? 1:i+1); add(now,get(xx,j)+n*m,1,1);
			    yy=(j+1>m ? 1:j+1); add(now,get(i,yy)+n*m,1,1);
			}
			else if (ch[j-1]=='D'){
				xx=(i-1==0 ? n:i-1); add(now,get(xx,j)+n*m,1,1);
				yy=(j-1==0 ? m:j-1); add(now,get(i,yy)+n*m,1,1);
				xx=(i+1>n ? 1:i+1); add(now,get(xx,j)+n*m,1,0);
			    yy=(j+1>m ? 1:j+1); add(now,get(i,yy)+n*m,1,1);
			}
			else if (ch[j-1]=='R'){
				xx=(i-1==0 ? n:i-1); add(now,get(xx,j)+n*m,1,1);
				yy=(j-1==0 ? m:j-1); add(now,get(i,yy)+n*m,1,1);
				xx=(i+1>n ? 1:i+1); add(now,get(xx,j)+n*m,1,1);
			    yy=(j+1>m ? 1:j+1); add(now,get(i,yy)+n*m,1,0);
			}
			else
			{
				xx=(i-1==0 ? n:i-1); add(now,get(xx,j)+n*m,1,1);
				yy=(j-1==0 ? m:j-1); add(now,get(i,yy)+n*m,1,0);
				xx=(i+1>n ? 1:i+1); add(now,get(xx,j)+n*m,1,1);
			    yy=(j+1>m ? 1:j+1); add(now,get(i,yy)+n*m,1,1);
			}
		}
	}
	doit();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值