BZOJ 3171 TJOI 2013 循环格 费用流

题目大意:给出一个表格,每个表格指向周围四个格子中的一个,问你可以改变一些格子上的指向,问让所有格子都在圈中最小需要改变多少。

思路:所有的格子都在圈中,由于每个格子只能有一个出边,所以就要保证所有格子都有一个入边。建立费用流的模型,所有点向汇点连流量1费用0的边,表示要接受一个入边。S向所有点连一条流量1费用0的边,表示一条出边。一个格子向周围四个格子连边,流量1,如果方向与当前方向相符,那么费用0,否则费用1。之后跑费用流就是答案了。

CODE:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 10010
#define INF 0x3f3f3f3f
#define S 0
#define T (MAX - 1)
using namespace std;
const int dx[] = {0,0,0,-1,1};
const int dy[] = {0,-1,1,0,0};
int G[256];

struct MinCostMaxFlow{
	int head[MAX],total;
	int next[MAX],aim[MAX],cost[MAX],flow[MAX];

	int f[MAX],from[MAX],p[MAX];
	bool v[MAX];

	MinCostMaxFlow() {
		total = 1;
	}
	void Add(int x,int y,int f,int c) {
		next[++total] = head[x];
		aim[total] = y;
		flow[total] = f;
		cost[total] = c;
		head[x] = total;
	}
	void Insert(int x,int y,int f,int c) {
		Add(x,y,f,c);
		Add(y,x,0,-c);
	}
	bool SPFA() {
		static queue<int> q;
		while(!q.empty())	q.pop();
		memset(f,0x3f,sizeof(f));
		memset(v,false,sizeof(v));
		f[S] = 0;
		q.push(S);
		while(!q.empty()) {
			int x = q.front(); q.pop();
			v[x] =  false;
			for(int i = head[x]; i; i = next[i])
				if(flow[i] && f[aim[i]] > f[x] + cost[i]) {
					f[aim[i]] = f[x] + cost[i];
					if(!v[aim[i]])
						v[aim[i]] = true,q.push(aim[i]);
					from[aim[i]] = x;
					p[aim[i]] = i;
				}
		}
		return f[T] != INF;
	}
	int EdmondsKarp() {
		int re = 0;
		while(SPFA()) {
			int max_flow = INF;
			for(int i = T; i != S; i = from[i])
				max_flow = min(max_flow,flow[p[i]]);
			for(int i = T; i != S; i = from[i]) {
				flow[p[i]] -= max_flow;
				flow[p[i]^1] += max_flow;
			}
			re += max_flow * f[T];
		}
		return re;
	}
}solver;

int m,n;
int num[20][20],cnt;
char s[20][20];

int main()
{
	G['L'] = 1,G['R'] = 2,G['U'] = 3,G['D'] = 4;
	cin >> m >> n;
	for(int i = 1; i <= m; ++i) {
		scanf("%s",s[i] + 1);
		for(int j = 1; j <= n; ++j) {
			num[i][j] = ++cnt;
			solver.Insert(S,cnt << 1,1,0);
			solver.Insert(cnt << 1|1,T,1,0);
		}
	}
	for(int i = 1; i <= m; ++i)
		for(int j = 1; j <= n; ++j)
			for(int k = 1; k <= 4; ++k) {
				int fx = i + dx[k],fy = j + dy[k];
				if(!fx)	fx = m;
				if(!fy)	fy = n;
				if(fx > m)	fx = 1;
				if(fy > n)	fy = 1;
				if(k == G[s[i][j]])
					solver.Insert(num[i][j] << 1,num[fx][fy] << 1|1,1,0);
				else
					solver.Insert(num[i][j] << 1,num[fx][fy] << 1|1,1,1);
			}
	cout << solver.EdmondsKarp() << endl;
	return 0;
}

时间: 2024-12-06 16:57:29

BZOJ 3171 TJOI 2013 循环格 费用流的相关文章

BZOJ 3171: [Tjoi2013]循环格( 费用流 )

每个点都在某个环中, 出度都为1, 只要让入度也全为1就可以满足题意了. 然后就是裸的最小费用最大流了. ---------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<deque> #include<bitset> using n

【BZOJ 3171】 [Tjoi2013]循环格

Description 一个循环格就是一个矩阵,其中所有元素为箭头,指向相邻四个格子.每个元素有一个坐标(行,列),其中左上角元素坐标为(0,0).给定一个起始位置(r,c) ,你可以沿着箭头防线在格子间行走.即如果(r,c)是一个左箭头,那么走到(r,c-1);如果是右箭头那么走到(r,c+1):如果是上箭头那么走到(r-1,c):如果是下箭头那么走到(r+1,c):每一行和每一列都是循环的,即如果走出边界,你会出现在另一侧.一个完美的循环格是这样定义的:对于任意一个起始位置,你都可以i沿着箭

BZOJ 3876 支线剧情 | 有下界费用流

BZOJ 3876 支线剧情 | 有下界费用流 题意 这题题面搞得我看了半天没看懂--是这样的,原题中的"剧情"指的是边,"剧情点"指的才是点. 题面翻译过来大概是这样: 有一个DAG,每次从1号点出发,走过一条路径,再瞬移回1号点.问:想要遍历所有的边,至少要走多少路程(瞬移回1号点不算路程). 题解 我们用有上下界费用流的模型,建个图: 原图中的每条边,流量范围是\([1, +\infty]\),表示至少走一次,可以走无限次,这条边的费用就是边权. 原图中的每个

[BZOJ 1221] [HNOI2001] 软件开发 【费用流 || 三分】

题目链接:BZOJ - 1221 题目分析 算法一:最小费用最大流 首先这是一道经典的网络流问题.每天建立两个节点,一个 i 表示使用毛巾,一个 i' 表示这天用过的毛巾. 然后 i 向 T 连 Ai (第 i 天需要的毛巾数).从 S 向 i' 连 Ai ,这样这天新增的用过的毛巾就是 Ai 了. 然后 i' 可以连向 (i+1)' ,表示留到下一天再处理,i' 还可以流向 i+p+1 和 i+q+1,表示洗了之后再次使用,这两种边是有费用的. 还有就是新购买毛巾,从 S 向 i 连,费用就是

BZOJ 3170: [Tjoi 2013]松鼠聚会( sort )

题目的距离为max(|x1-x2|, |y1-y2|) (切比雪夫距离). 切比雪夫距离(x, y)->曼哈顿距离((x+y)/2, (x-y)/2) (曼哈顿(x, y)->切比雪夫(x+y, x-y)). 转成Manhattan distance后排序前缀和维护即可. -------------------------------------------------------------------------- #include<cstdio> #include<cs

BZOJ 1930 Shoi2003 pacman 吃豆豆 费用流

题目大意:给定一个平面上的一些点,吃豆先生从原点出发,只能向右或向上走,求两个吃豆先生最多吃到多少豆 每个点拆成两个,之间连一条流量为1,费用为1的边: 如果从一个点出发可以到达另一个点,就将前一个点的出点连向后一个点的入点 跑费用流.但是这样显然是会TLE的 如果i能到j,j能到k,那么显然无需连i->k这条边 这是一个剪枝 加了这个剪枝之后可能会WA 因此还要考虑一个点经过多次的情况 即每个点从入点向出点再连一条流量为1,费用为0的边 加了这个之后就能过了 剪枝不强 但是没有什么情况能把这个

BZOJ 2245 SDOI 2011 工作安排 费用流

题目大意:有一些商品需要被制造,有一些员工,每一个员工会做一些物品,然而这些员工做物品越多,他们的愤怒值越大,这满足一个分段函数.给出哪些员工可以做哪些东西,给出这些分段函数,求最小的愤怒值以满足需要被制造的商品. 思路:费用流.我写的朴素费用流好像很慢,有时间学一学费用流的多路增广. 由于题目中满足那些分段函数是满足单调递增的性质的,所以就可以如下建图: S->每个人,费用0,流量INF 每个商品->T,费用0,流量为需要改商品的数量 对于每个人虚拟建n个节点(n<=5) 每个人-&g

BZOJ 1927 SDOI 2010 星际竞速 费用流

题目大意:宇宙空间中进行了一次竞速大赛.有两种飞行方式,第一种是通过正常的道路,但是只能从标号小的飞到标号大的地方:第二种是直接过去,但是需要花费固定的时间.问正好遍历一次所有的点最少需要的多少时间. 思路:费用流.把每个点拆点,S到每个点的起点连费用0的边,向每个终点连费用为固定费用的边,图中原有的边从一个的起点连到另一个点的终点.然后每个点的终点向T连边.跑最小费用最大流就是最后的答案. CODE: #include <queue> #include <cstdio> #inc

【BZOJ】1070: [SCOI2007]修车(费用流+特殊的技巧)

http://www.lydsy.com/JudgeOnline/problem.php?id=1070 好神的题!!!orz 首先我是sb不会拆点..... 首先,每一个技术人员维修车辆都有一个先后顺序,那么我们将技术人员每一次维修的顺序拆点!!即如果有n辆车,那么每个技术人员就拆成n个点向每个车子连边,容量为1,费用依次为k*时间,即表示如果第k次修这架车,那么这架车一定只等待了k*时间那么久.. 然后就行了.. (注意数据读入的顺序......没看清就会wa... #include <cs