大航海 解题报告

大航海

Time Limit:1000MS  Memory Limit:65536K

Description

你的船队进入了一个河道,河道的两边布满繁华的港口。你和你的手下都非常渴望马上进入这些港口进行贸易,以获得利润,但是流域的管理者却告诉你在他们的地盘航行必须遵守他们的法令。河道左岸的 N 个港口被编号为 L1,L2...LN,河道右岸的 M 个港口被编号为 R1,R2...RM。法令规定如果你进入过左岸(右岸)的某个港口 Li(Ri),你就不能再进入编号为 L1,L2...Li(R1,R2...Ri)的港口。法令还规定了 P 条航道,每条航道(i,j)表示你可以从 Li 港驶到 Rj 港,或者从 Rj 港驶到 Li 港。其它港口之间的航行都是不被允许的。一开头你可以选择停靠在任意一个港口,并且任何时候都可以离开这个流域。一旦离开了这个流域,你就不会再回来了。

给定进入每个港口能获取的利润,你能够迅速估计你最多总共能够获得多少利润?

Input

每个输入文件只包含一组数据。

每个文件的第一行包含三个正整数 N,M,P。1≤N,M≤10000,0≤P≤500000。

以下 N 行,每行一个非负整数,第 i 行表示在港口 Li 能获得的利润。以下 M 行,每行一个非负整数,第 i 行表示在港口 Ri 能获得的利润。所有利润的总和不会超过 10^9。

再下来 P 行,每行两个整数 i,j,表示一个航道。

Output

输出一行,表示能够获得的最大的总利润。

Sample Input

3	3	5
5		
0		
10		
10		
10		
0		
1	2	
3	1	
2	2	
2	3	
3	2	

Sample Output

30

Source

GDKOI2007


刚开始看这道题的时候我是想到用网络流的,不过有一些性质,可以将这题简化。

从这道题的信息,我们可以看出行走路线中,左边的港口和右边的港口编号都是单调递增的。因此,我们可以利用这一个性质去构造一个dp方程。

比较麻烦的是港口分左右两岸,处理上我们不妨分两个dp数组解决。

先单独考虑左岸港口,由于必须保证港口编号递增,将左岸港口从1到n升序枚举;

若左岸i港口与右岸k港口相连,那么显然

dp_left[i]=max(dp_left[i],dp_right[k]+profit[i][k])

由于i是升序的,方程无后效性。

再考虑右岸港口在第一重循环枚举左岸港口的情况下,升序枚举与i相连的右岸港口j,则:

dp_right[j]=max(dp_right[j],dp_left[i]+profit[i][j])

统一一下,就写成下面的形式:

for each left //枚举left左港口

    for each edge(left,right) //升序枚举right右港口

    {

        tmp=dp_left[left]; //dp_left[left]更新前先存值用于更新dp_right[right];

        update(dp_left[left]); //dp_left[i]=max(dp_left[i],dp_right[k]+profit[i][k])

        update(dp_right[right]); //dp_right[j]=max(dp_right[j],tmp+profit[i][j])

    }

由于可以在任意港口离开,答案即为max{max{dp_left[i]},max{dp_right[j]}},1<=i<=n,1<=j<=m;

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
	int edge[500001][2];
	int n,m,p,cnt,ans;
	int left[10001];
	int right[10001];
	int label[10001];
	int len[10001];
	int dp1[10001],dp2[10001];
void qs(int l,int r)
{
	int i=l;
	int j=r;
	int m=edge[(l+r)/2][0];
	int next=edge[(l+r)/2][1];
	while (i<=j)
	{
		while (edge[i][0]<m||(edge[i][0]==m&&edge[i][1]<next)) i++;
		while (edge[j][0]>m||(edge[j][0]==m&&edge[j][1]>next)) j--;
		if (i<=j)
		{
			int k=edge[i][0];
			edge[i][0]=edge[j][0];
			edge[j][0]=k;
			k=edge[i][1];
			edge[i][1]=edge[j][1];
			edge[j][1]=k;
			i++;
			j--;
		}
	}
	if (i<r) qs(i,r);
	if (j>l) qs(l,j);
}
int main()
{
	scanf("%d%d%d",&n,&m,&p);
	for (int i=1;i<=n;i++) scanf("%d",&left[i]);
	for (int i=1;i<=m;i++) scanf("%d",&right[i]);
	for (int i=1;i<=p;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		edge[++cnt][0]=x;
		edge[cnt][1]=y;
	}
	qs(1,cnt);
	label[edge[1][0]]=1;
	for (int i=2;i<=cnt;i++)
		if (edge[i][0]!=edge[i-1][0])
		{
			len[edge[i-1][0]]=i-label[edge[i-1][0]];
			label[edge[i][0]]=i;
		}
	len[edge[cnt][0]]=cnt-label[edge[cnt][0]]+1;
	for (int i=1;i<=n;i++) dp1[i]=left[i];
	for (int i=1;i<=m;i++) dp2[i]=right[i];
	for (int i=1;i<=n;i++)
	{
		int t=label[i];
		for (int j=t;j<=t+len[i]-1;j++)
		{
			int k=edge[j][1];
			int tmp=dp1[i];
			dp1[i]=max(dp1[i],dp2[k]+left[i]);
			dp2[k]=max(dp2[k],tmp+right[k]);
		}
	}
	for (int i=1;i<=n;i++) ans=max(ans,dp1[i]);
	for (int i=1;i<=m;i++) ans=max(ans,dp2[i]);
	printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/weixin_39872717/article/details/80094787