国防科技大学2019校赛G Altitude(平面图转对偶图找最小割)

8 篇文章 0 订阅
2 篇文章 0 订阅

国防科技大学2019校赛G Altitude(平面图转对偶图找最小割)

https://ac.nowcoder.com/acm/contest/878/G

题目大意

给出一副网格,其中每条边上往来都有一些人流,网格的每个交叉点都有一个高度,网格的每条边连接着两个交叉点,人流的代价为向高处走的高度差乘上人流的人数。向低处或者相同高度处走不需要付出代价。网格的左上角的高度为0,网格右下角的高度为1。问整张图的最小代价为多少

解题思路

由于下降和相同高度之间往来不需要代价,因此,网格交点的高度只可能是0或者1,更进一步说,只需要对原本的网格图划分成两部分即可,一部分包含左上角高度都为0,另一部分包含右下角高度都为1,这样就只有两部分交界处的边有费用。那么求整张图的最小费用就变成了求图的一个划分,使得划分的边的权值和最小,也就变成了求一个最小割。由于本题数据范围的限制以及原图为一平面图,因此可以将原图转化为对偶图,通过求最短的的方式求最小割

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1ll<<60;
const int maxn=3e5+10;

struct Edge{
	int v,nxt;
	ll w;
} edge[maxn<<2];
int head[maxn],cnt;
ll dis[maxn];
int n;
struct Node{
	int id;
	ll w;
	bool  operator <(const Node &b) const {return w>b.w;}
};
void Init()
{
	memset(head,-1,sizeof(head));
	cnt=0;
}
void addedge(int u,int v,ll w)
{
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}
void Dij(int s)
{
	for(int i=0;i<=n*n+1;++i) dis[i]=INF;
	dis[s]=0;
	priority_queue<Node> q;
	q.push(Node{s,0});
	while(!q.empty())
	{
		Node u=q.top();q.pop();
		int Id=u.id;
		if(dis[Id]!=u.w) continue;
		for(int i=head[Id];~i;i=edge[i].nxt)
		{
			int v=edge[i].v;
			if(dis[v]>dis[Id]+edge[i].w)
			{
				dis[v]=dis[Id]+edge[i].w;
				q.push(Node{v,dis[v]});
			}
		}
	}
}

int main()
{
	while(~scanf("%d",&n))
	{
		Init();
		int s=0,t=n*n+1;
		ll val;
		// w-->e
		for(int i=1;i<=n;++i) scanf("%lld",&val),addedge(i,t,val);
		for(int i=1;i<=n*(n-1);++i)
		{
			scanf("%lld",&val);
			//addedge(i,i+n,val);
			addedge(i+n,i,val);
		}
		for(int i=1;i<=n;++i) scanf("%lld",&val),addedge(s,n*(n-1)+i,val);
		
		for(int i=1;i<=n;++i)
		{
			for(int j=0;j<=n;++j)
			{
				scanf("%lld",&val);
				if(j==0){addedge(s,(i-1)*n+1,val);}//×ó²à 
				else if(j==n){addedge(i*n,t,val);}// you
				else {addedge(j+n*(i-1),j+n*(i-1)+1,val);}// zhong
			}
		}
		
		//e-->w
		
		for(int i=1;i<=n;++i) scanf("%lld",&val),addedge(t,i,val);
		for(int i=1;i<=n*(n-1);++i)
		{
			scanf("%lld",&val);
			addedge(i,i+n,val);
			//addedge(i+n,i,val);
		}
		for(int i=1;i<=n;++i) scanf("%lld",&val),addedge(n*(n-1)+i,s,val);
		
		//s-->n
		for(int i=1;i<=n;++i)
		{
			for(int j=0;j<=n;++j)
			{
				scanf("%lld",&val);
				if(j==0){addedge((i-1)*n+1,s,val);}//×ó²à 
				else if(j==n){addedge(t,i*n,val);}// you
				else {addedge(j+n*(i-1)+1,j+n*(i-1),val);}// zhong
			}
		}
		// n-->s
		
		
		//for(int i=1;i<=n*(n+1);++i) scanf("%d",&val);
		Dij(s); 
		printf("%lld\n",dis[t]);
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值