石油大 2019年我能变强组队训练赛第一场 11035 Problem D Mining Station on the Sea(最小费用最大流)

链接:http://icpc.upc.edu.cn/problem.php?cid=1690&pid=3

题意:多组样例。n个船,n个港口,m个灯塔,灯塔之间有路(p条),灯塔和港口之间有路(k条)。输入为,第一行n、m、p、k,第二行给出n个数,表示船初始在哪个灯塔,接下来p行描述灯塔之间的路,接下来k行描述港口和灯塔(注意顺序)之间的路。一个港口只能容纳一条船,求每条船到港口的路径的最小总和。

思路:很明显的费用流。源点和有船的灯塔相连,容量为在该灯塔的船的个数,费用为0;灯塔之间的路,容量设为无穷大,费用设为长度;港口和灯塔之间的路,容量设为无穷大,费用设为长度;每个港口和汇点连边,容量为1,费用为0。跑一遍最小费用最大流即可。

#include <bits/stdc++.h>
#include <iostream>
#define ll long long
using namespace std;
const int N = 1e3+10; 
const int M = 3e5+10;
const int inf = 0x3f3f3f3f;
int n,m;
struct node
{
	int from,to,ca,w,next;
}g[M];
struct Node
{
	int to;
	ll dis;
	Node(){}
	Node(int to,ll dis):to(to),dis(dis){}
	friend bool operator <(Node a,Node b)
	{
		return a.dis>b.dis;
	}
};
int head[N],cnt,pre[N],mp[N];
ll dis[N];
bool vis[N];
void Init()
{
	cnt=0;
	for(int i=0;i<=n+m+1;i++)
		head[i]=-1,mp[i]=0;
	return ;
}
void add(int u,int v,int ca,int w)
{
	g[cnt].from=u;
	g[cnt].to=v;
	g[cnt].ca=ca;
	g[cnt].w=w;
	g[cnt].next=head[u];
	head[u]=cnt++;
	g[cnt].from=v;
	g[cnt].to=u;
	g[cnt].ca=0;
	g[cnt].w=-w;
	g[cnt].next=head[v];
	head[v]=cnt++;
	return ;
}

bool spfa(int s,int t)
{
	//memset(dis,inf,sizeof(dis));
	//memset(vis,0,sizeof(vis));
	for(int i=s;i<=t;i++)
	{
		dis[i]=1e18;
		vis[i]=0;
	}
	priority_queue<Node> q;
	Node now;
	int u,v;
	q.push(Node(s,0));
	dis[s]=0;
	vis[s]=1;
	pre[s]=-1;
	while(!q.empty())
	{
		
		now=q.top();
		//cout<<now.to<<":"<<endl;
		q.pop();
		u=now.to;
		vis[u]=0;
		for(int i=head[u];i!=-1;i=g[i].next)
		{
			
			v=g[i].to;
			//cout<<v<<" "<<endl;
			if(g[i].ca>0&&dis[v]>dis[u]+g[i].w)
			{
				pre[v]=i;
				dis[v]=dis[u]+g[i].w;
				if(!vis[v])
				{
					vis[v]=1;
					q.push(Node(v,dis[v]));
				}
			}
		}
	}
	return dis[t]!=1e18;
}
ll MCMF(int s,int t)
{
	
	ll mincost=0;
	int maxflow=0,now;
	while(spfa(s,t))
	{	
		now=t;
		maxflow=inf;
		while(now!=s)
		{
			maxflow=min(maxflow,g[pre[now]].ca);
			now=g[pre[now]].from;
		}
		now=t;
		while(now!=s)
		{
			g[pre[now]].ca-=maxflow;
			g[pre[now]^1].ca+=maxflow;
			mincost+=(ll)maxflow*g[pre[now]].w;
			now=g[pre[now]].from;
		}
	}
	return mincost;
}
int main(void)
{
	int x,u,v,w,k,p;
	while(~scanf("%d%d%d%d",&n,&m,&k,&p))
	{
		Init();
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&x);
			mp[x]++;	
		}
		for(int i=1;i<=m;i++)
		{
			if(mp[i]) 
				add(0,i,mp[i],0);
		}
		for(int i=1;i<=k;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			add(u,v,inf,w);
			add(v,u,inf,w);
		}
		for(int i=1;i<=p;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			add(v,m+u,inf,w);
		}
		for(int i=1;i<=n;i++)
		{
			add(m+i,n+m+1,1,0);
		}		
		printf("%lld\n",MCMF(0,n+m+1));
	}
	return 0;	
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值