危险的迷宫 网络流

【 问题描述】
近来发现了一个古老的地下迷宫,已探明该迷宫是一个 A 行 B 列的矩阵,该迷宫有 N
个不同的出口与 N 个不同的入口,任一单元格不会既为入口又为出口。为了进一步探明与
发掘该迷宫,N 个考古队员分别从地上的 N 个不同的入口进入迷宫,并且计划从 N 个不同
的出口出来。每个队员任意选择一个出口出来,但任意两名队员不会选择同一个出口。
迷宫中的每一格与其相邻的某些格相通。该迷宫设计非常精妙,在不知道具体机关的情
况下,人一旦离开其所在格后,该格将迅速关闭,且再也不能开启,也就是说每一格仅能进
入一次。更糟的是,迷宫中的每一格都有一定的危险性,专家们用 1 至 100 的整数表示,数
值越大表示越危险。正因为如此,再加之每一格都不很宽敞,两人一起进入比较危险,所以
规定不能两个人同时进入同一格。
为了队员们的安全着想,希望你能够编程求出如何使队员们所经过单元格的危险性总和
最小。
有如下迷宫:
每一格中的数字表示该格的危险程度。两格间若有空缺,表示这两格相通。
入口有两个:
(1,1)即第一行第一列,(1,2)即第一行第二列
出口也有两个:
(2,3)即第二行第三列,
(3,4)即第三行第四列
两名队员的最好的行动方案之一,如上图红蓝箭头所示。危险程度之和最小为 235。
【输入描述】
第一行是两个整数 A 与 B(1≤A,B≤10),中间用空格分隔,表示该迷宫是 A 行 B 列的。
第 2 行至第 A+1 行,每行有 B 个 1 至 100 以内的整数,表示该迷宫每一格的危险程度。
以下一行是一个整数 K。接着 K 行每行有四个整数 X0,Y0,X1,Y1,(1 ≤X0,X1≤A, 1≤Y0,Y1
≤B) ,表示(X0,Y0),(X1,Y1)为相邻的两格,这两格互相相通。

 


接着一行是一个整数 N(0≤N≤A*B/2)
,表示有 N 个出口与入口,保证出入口不会重
合。
以下 N 行,每行有两个整数 X0,Y0,表示每个入口的行列位置。
以下还有 N 行,每行有两个整数 X1,Y1,表示每个出口的行列位置。
【输出描述】
输出仅一个数,若队员们不能全部到达指定目标位置,则输出-1;否则输出所有队员所
经过的所有单元格的危险程度之和。
【输入样例】
3 4
20 30 40 30
30 60 20 20
20 15 20 20
131 1 2 1
1 2 1 3
1 2 2 2
1 3 1 4
1 4 2 4
2 1 2 2
2 1 3 1
2 2 2 3
2 3 2 4
2 4 3 4
3 1 3 2
3 2 3 3
3 3 3 4
2
1 1
1 2
2 3
3 4
【输出样例】
235

 


 

这其实是一道最小费用最大流的‘模板题’.

代码:

 

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

#define ll long long
#define il inline
#define db double

using namespace std;

il int gi()
{
	int x=0,y=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
		{
			if(ch=='-')
				y=-1;
			ch=getchar();
		}
	while(ch>='0'&&ch<='9')
		{
			x=x*10+ch-'0';
			ch=getchar();
		}
	return x*y;
}

int n,m;

int src,des;

int head[200045],cnt=1;

struct edge
{
	int next,to,cap,cost;
}e[200045];

il void add(int from,int to,int cap,int cost)
{
	e[++cnt].next=head[from];
	e[cnt].to=to;
	e[cnt].cap=cap;
	e[cnt].cost=cost;
	head[from]=cnt;
}

int map[45][45];

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

il void build(int from,int to,int cap,int cost)//将双向边
{
	add(from,to,cap,cost);//正边
	add(to,from,0,-cost);//负边
}

int ans;

int flow;

int t[100045];

int headd,tail;

bool vis[100045];

int dist[100045],pre[100045];//pre保存边的编号

il bool bfs()
{
	memset(dist,127/3,sizeof(dist));
	headd=0,tail=1;
	t[0]=src;
	dist[src]=0;
	vis[src]=1;
	while(headd!=tail)
		{
			int h=t[headd++];
			vis[h]=0;
			int r=head[h];
			while(r!=-1)
				{
					int now=e[r].to;
					if(dist[now]>dist[h]+e[r].cost&&e[r].cap>0)
						{
							dist[now]=dist[h]+e[r].cost;
							pre[now]=r;
							if(!vis[now])
								{
									vis[now]=1;
									t[tail++]=now;
								}
						}
					r=e[r].next;
				}
		}
	if(dist[des]!=dist[100000])
		return 1;
	else
		return 0;
}

il void back()
{
	int now=des;
	while(now!=src)
		{
			ans+=e[pre[now]].cost;
			e[pre[now]].cap--;
			e[pre[now]^1].cap++;
			now=e[pre[now]^1].to;
		}
}

il void min_flow()
{
	while(bfs())//可以跑到终点
		{
			back();//把路上的边都更新
			flow++;
		}
}

int main()
{
	freopen("maze.in","r",stdin);				
	freopen("maze.out","w",stdout);

	memset(head,-1,sizeof(head));

	n=gi(),m=gi();

	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			{
				map[i][j]=gi();
				build(get_node(i,j),get_node(i,j)+n*m,1,map[i][j]);//拆点建边
			}

	int k=gi();
	int x1,y1,x2,y2;
	for(int i=1;i<=k;i++)
		{
			x1=gi(),y1=gi(),x2=gi(),y2=gi();
			build(get_node(x1,y1)+n*m,get_node(x2,y2),1,0);
			build(get_node(x2,y2)+n*m,get_node(x1,y1),1,0);
		}

	int q=gi();
	int x,y;
	src=0,des=n*m*2+1;
	for(int i=1;i<=q;i++)
		{
			x=gi(),y=gi();
			build(src,get_node(x,y),1,0);
		}
	
	for(int i=1;i<=q;i++)
		{
			x=gi(),y=gi();
			build(get_node(x,y)+n*m,des,1,0);
		}

	min_flow();//跑费用流
	
	if(flow==q)
		printf("%d\n",ans);
	else
		printf("-1\n");

	return 0;
}

 

posted @ 2017-10-25 16:38  GSHDYJZ  阅读(362)  评论(0编辑  收藏  举报