【bzoj3171】[Tjoi2013]循环格

3171: [Tjoi2013]循环格

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 1149   Solved: 727
[ Submit][ Status][ Discuss]

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

Source

[ Submit][ Status][ Discuss]

观察到,最终的循环格中,每个点肯定只有一个初度和一个入度


那么可以考虑如下建图:


把每个点拆成x,y两个点,如果两点u,v之间本来就有边,那么从xu到yv连一条容量为1,代价为0 的边,如果无边并且相邻,那么连一条容量为1,代价为1的边,


然后把s点连向所有x点,容量为1,代价为0,t点也同理


然后跑最小费用最大流


代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;

typedef long long LL;
typedef double DL;

const int INF = 2147483647;
const int maxn = 20;
const int maxp = maxn * maxn * 2;
const int maxm = maxp * maxp * 2;
const int d[4][2] = {{-1,0},{1,0},{0,1},{0,-1}};

struct data{
	int to,cap,flow,c;
}e[maxm];

vector<int> G[maxp];
int n,m,s,t,tot,tote = -1,ans;
int x[maxn][maxn],y[maxn][maxn];
int pri[300],dis[maxp],a[maxp],pre[maxp],exist[maxp],Q[maxp];
char gra[maxn][maxn];

inline int getint()
{
	int ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9') 
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline int spfa()
{
	memset(a,0,sizeof(a));
	memset(pre,0,sizeof(pre));
	memset(exist,0,sizeof(exist));
	for (int i = 1; i <= tot; i++) dis[i] = INF;
	int head = 0,tail = 0;
	dis[Q[++tail] = s] = 0;
	a[s] = INF;
	while (head < tail)
	{
		int u = Q[++head];
		exist[u] = 0;
		for (int i = 0; i < G[u].size(); i++)
		{
			data E = e[G[u][i]];
			if (E.cap == E.flow) continue;
			if (dis[E.to] > dis[u] + E.c)
			{
				a[E.to] = min(a[u],E.cap - E.flow);
				dis[E.to] = dis[u] + E.c;
				pre[E.to] = G[u][i] ^ 1;
				if (!exist[E.to])
					exist[Q[++tail] = E.to] = 1;
			}
		}
	}
	if (!a[t]) return 0;
	int now = t;
	while (now != s)
	{
		e[pre[now] ^ 1].flow += a[t];
		e[pre[now]].flow -= a[t];
		now = e[pre[now]].to;
	}
	ans += a[t] * dis[t];
	return 1;
}

inline void add(int u,int v,int cap,int c)
{
	e[++tote] = (data){v,cap,0,c};
	G[u].push_back(tote);
	e[++tote] = (data){u,cap,cap,-c};
	G[v].push_back(tote);
}

inline char getcom()
{
	char c = getchar();
	while (c != 'U' && c != 'R' && c != 'D' && c != 'L') c = getchar();
	return c;
}

int main()
{
	pri['U'] = 0; pri['D'] = 1; pri['R'] = 2; pri['L'] = 3;
	n = getint(); m = getint();
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			x[i][j] = ++tot , y[i][j] = ++tot;
	for (int i = 1; i <= n; i++)
		y[i][0] = y[i][m] , y[i][m + 1] = y[i][1];
	for (int j = 1; j <= m; j++)
		y[0][j] = y[n][j] , y[n + 1][j] = y[1][j];
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			gra[i][j] = getcom();
	
	s = ++tot; t = ++tot;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			for (int k = 0; k <= 3; k++)
				add(x[i][j],y[i + d[k][0]][j + d[k][1]],1,pri[gra[i][j]] != k);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			add(s,x[i][j],1,0);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			add(y[i][j],t,1,0);
	while (spfa());
	printf("%d",ans);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值