hdu3870 S-T平面图最小割转化为最短路

        题意:N*N的方格,每条边有权值,求从(1,1)到(n,n)的最小割。n<=400

        这题是在S-T平面图中将最小割转化为最短路的题,推荐08年OI论文周冬《两极相通——浅析最大—最小定理在信息学竞赛中的应用》,没看论文的压力很大,果断不会。研究了一会,下面说下自己的理解。

何为S-T平面图:首先是一平面图(满足欧拉公式与存在对偶图),且源点S,汇点T在边界上。

如何构造对偶图:将S-T连线,将最外面的一个大面(无限大)一分为二了,一个为S*,一个为T*,然后将跨跃某条边的两个面连线,再去掉S*与T*的连线。从下图就可看出S*到T*的一条路径就会对应一个S至T割,S*到T*的最短路就对应最小割了,然后求S*到T*的最短路即可。如图

 

#include<iostream>
#include<vector>
#include<queue>
using namespace std;

const int maxn=170000;

struct point
{
	int v,w;
}p0;

vector<point> e[maxn];
//queue<int>q;//
int n,S,T,q[maxn];
int dist[maxn];
bool inq[maxn];
int SPFA()
{
	int i,k,head=0,tail=0;

	memset(inq,false,sizeof(inq));
	for(i=0;i<=(n-1)*(n-1)+1;i++)
		dist[i]=0x3fffffff;
//	while(!q.empty()) q.pop();
//	q.push(S);
	q[tail++]=S;
	dist[S]=0;
	inq[S]=true;

	while(head!=tail)
	{
	//	k=q.front();
	//	q.pop();
		k=q[head];
		head=(head+1)%maxn;
		inq[k]=false;
		for(i=0;i<e[k].size();i++)
		{
			p0=e[k][i];
			if(dist[p0.v]>dist[k]+p0.w)
			{
				dist[p0.v]=dist[k]+p0.w;
				if(!inq[p0.v])
				{
					inq[p0.v]=true;
					q[tail]=p0.v;
					tail=(tail+1)%maxn;
				//	q.push(p0.v);
				}
			}
		}
	}
	return dist[T];
}

int main()
{
	int t,i,j,k;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(i=0;i<(n-1)*(n-1)+2;i++)
			e[i].clear();
		S=(n-1)*(n-1);
		T=(n-1)*(n-1)+1;
		for(i=0;i<n;i++)
		{
			for(j=0;j<n;j++)
			{
				scanf("%d",&k);
				p0.w=k;
				if(i==0&&j!=n-1)
				{
					p0.v=j;
					e[S].push_back(p0);
				//	p0.v=S;
				//	e[j].push_back(p0);
				}
				if(j==n-1&&i!=n-1)
				{
					p0.v=i*(n-1)+j-1;
					e[S].push_back(p0);
				//	p0.v=S;
				//	e[i*(n-1)+j-1].push_back(p0);
				}
				if(j==0&&i!=n-1)
				{
					p0.v=T;
					e[i*(n-1)].push_back(p0);
				//	p0.v=i*(n-1);
				//	e[T].push_back(p0);
				}
				if(i==n-1&&j!=n-1)
				{
					p0.v=T;
					e[(n-2)*(n-1)+j].push_back(p0);
				//	p0.v=(n-2)*(n-1)+j;
				//	e[T].push_back(p0);
				}

				if(i!=n-1&&j!=n-1)
				{
					if(i)
					{
						p0.v=(i-1)*(n-1)+j;
						e[i*(n-1)+j].push_back(p0);
						p0.v=i*(n-1)+j;
						e[(i-1)*(n-1)+j].push_back(p0);
					}
					if(j)
					{
						p0.v=i*(n-1)+j-1;
						e[i*(n-1)+j].push_back(p0);
						p0.v=i*(n-1)+j;
						e[i*(n-1)+j-1].push_back(p0);
					}
				}
			}
		}

		printf("%d\n",SPFA());
	}
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值