洛谷 P4237 荒芜的海洋

题目描述

这块海洋上有n个小岛,小岛有m座石桥相连。有一些小岛上有wzt埋下的奖赏,它们非常诱人。它们的诱惑力用整数ki描述。而一些小岛上有lsq的雇佣兵,他们有一个价格,用整数bi描述。lsq必须花钱,他的雇佣兵才会帮他寻找奖赏。

雇佣兵的价格并不会变。对于每一个雇佣兵,在寻找过程中,他会越过一座座的桥,这过程中,他的价格会 加上他所经过的所有桥的长度 。

遗憾的是,不只有桥的阻挡,每座岛上有许多猛兽,虽然雇佣兵们都英勇无比,但驱逐猛兽的过程会让人很不爽。因此,对于每一个雇佣兵,价格会 加上他所经过的所有岛(包括出发岛)上的猛兽数量之和。

lsq了解这里的一切情况,他需要做出决策,即决定他的每个雇佣兵应该去找哪个奖赏。lsq的目的是找到所有奖赏,并取得最大收益。每个雇佣兵只能雇佣一次。

收益的定义为: 所有奖赏的诱惑力减去lsq花的所有的钱

lsq的决策异常艰难,于是只好请 AK过NOI 的你来帮忙。

输入格式

第一行4个数,n(小岛总数),m(桥总数),a(lsq的雇佣兵总人数),b(奖赏总数)

接下来一行n个数,表示每个小岛上的猛兽数量

接下来m行,每行三个数u,v,w,表示u号小岛与v号小岛之间有一座长度为w的桥相连

接下来a行,每行两个数qi,pi,表示i号雇佣兵价格为qi,初始位置为pi号小岛

接下来b行,每行两个数ki,qi,表示i号奖赏的诱惑力为ki,位置为qi号小岛

输出格式

如果能找到所有奖赏,输出“Yes”,并在下一行输出能达到的最大满意度。

如果不能找到所有奖赏,输出“No”,并在下一行输出最多能找到多少奖赏。

大致思路

大体为费用流

首先建图,源点向每个佣兵连容量为1,费用为0的边,如果,代表是否雇佣这个人,而每个佣兵向他可以到达的奖赏连他的费用+桥长+野兽数(注意起点终点的野兽都需要计算),这些数值可以用Spfa统一计算,最后每个奖赏再向汇点连容量为1,费用为-ki的边,代表如果到达,可以赚取ki的费用。

最后遍历每个奖赏,若它连的边到汇点并且它现已没有流量,就说明这个奖赏可以到达,同意答案,最后输出。

最后注意,若佣兵到不了某个小岛,dis数组会为100000000,这时不能连边,否则答案会变的巨大不要问我是怎么知道的

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
#define MAXN 100100
int n, m, cnt, a[MAXN], src, sink, A, B, pri[MAXN], z[MAXN];
int comu[MAXN], flag[MAXN], dis[MAXN], cnt2, k[MAXN], q[MAXN];
struct node
{
	int v, w, c;
	node *next, *rev;
}pool[2*MAXN], *h[MAXN], *comv[MAXN];
void addedge(int u, int v, int c, int w)
{
	node *p = &pool[++cnt], *q = &pool[++cnt];
	p->v = v; p->w = w; p->c = c; p->next = h[u]; p->rev = q; h[u] = p;
	q->v = u; q->w = -w;q->c = 0; q->next = h[v]; q->rev = p; h[v] = q;
}
queue<int> Q;
bool spfa()
{
	for(int i=1;i<=sink;i++) dis[i] = (1<<30);
	memset(flag, 0, sizeof(flag));
	Q.push(src);
	dis[src] = 0;
	flag[src] = 1;
	while(!Q.empty())
	{
		int u = Q.front();
		Q.pop();
		flag[u] = 0;
		for(node *p = h[u]; p; p = p->next)
		{
			if(p->c > 0 && dis[p->v] > dis[u] + p->w)
			{
				dis[p->v] = p->w + dis[u];
				comu[p->v] = u;
				comv[p->v] = p;
				if(flag[p->v] == 0)
				{
					Q.push(p->v);
					flag[p->v] = 1;
				}
			}
		}
	}
	if(dis[sink] < (1<<30)) return true;
	else return false;
}
int find()
{
	int Min = 100000000;
	int u = sink;
	while(u != src)
	{
		Min = min(Min, comv[u]->c);
		u = comu[u];
	}
	u = sink;
	while(u != src)
	{
		comv[u]->c -= Min;
		comv[u]->rev->c += Min;
		u = comu[u];
	}
	return Min;
}
int ans1, ans2;
void mincostflow()//费用流 
{
	int sim=0;
	while(spfa() == true)
	{
		sim = find();
		ans1 += sim;
		ans2 += (sim*dis[sink]);
	}
}
struct edge
{
	int v, w;
	edge *next;
}pool2[2*MAXN], *h2[MAXN];
void addedge2(int u, int v, int w)
{
	edge *p = &pool2[++cnt2], *q = &pool2[++cnt2];
	p->v = v; p->w = w; p->next = h2[u]; h2[u] = p;
	q->v = u; q->w = w; q->next = h2[v]; h2[v] = q;
}
void Spfa(int s)//求每个佣兵到其他岛的费用 
{
	for(int i=1;i<=n;i++) dis[i] = 100000000;
	memset(flag, 0, sizeof(flag));
	Q.push(s);
	dis[s] = a[s];//初值为起点野兽数 
	while(!Q.empty())
	{
		int u = Q.front();
		Q.pop();
		flag[u] = 0;
		for(edge *p = h2[u]; p; p = p->next)
		{
			if(dis[p->v] > p->w + a[p->v] + dis[u])//这里直接加上了野兽数 
			{
				dis[p->v] = p->w + a[p->v] + dis[u];
				if(flag[p->v] == 0)
				{
					Q.push(p->v);
					flag[p->v] = 1;
				}
			}
		}
	}
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&A,&B);
	src = 0; sink = n+ m + 1 + A + B;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int u, v, w;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		addedge2(u, v, w);
	}
	for(int i=1;i<=A;i++) 
		scanf("%d%d",&pri[i],&z[i]);
	for(int i=1;i<=B;i++) 
		scanf("%d%d",&k[i],&q[i]);
	for(int i=1;i<=A;i++)
	{
		Spfa(z[i]);
		for(int j=1;j<=B;j++) 
			if(dis[q[j]] < 100000000) //必须要判,否则会被算在费用里 
				addedge(i, A+j, 1, dis[q[j]] + pri[i]);
	}
	for(int i=1;i<=A;i++)
		addedge(src, i, 1, 0);
	for(int i=1;i<=B;i++)
		addedge(i+A, sink, 1, -k[i]);
	mincostflow();
	int cnt3=0;
	for(int i=1+A;i<=A+B;i++)
	{
		for(node *p = h[i]; p; p = p->next)
		{
			if(p->v == sink && p->c == 0) cnt3++;//统计答案 
		}
	}
	if(cnt3 == B) printf("Yes\n%d\n",-ans2);
	else printf("No\n%d\n",cnt3);
 	
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/tuihuaddeming/p/11620652.html
今日推荐