1. 程式人生 > 實用技巧 >P1073 最優貿易 分層圖+最長路

P1073 最優貿易 分層圖+最長路

洛谷p1073 最優貿易

連結

首先易得暴n2的暴力,暴力列舉就行
顯然1e5的資料是會炸的
我們再分析題意,發現一共分為兩個個步驟,也可以說是狀態,即在一個點買入,在另一個點賣出,我們可以構建一個三層分層圖
第一層的每個點和第二層的對應點各連線一條權值為-val[i](val[i]表示i號點的水晶價格)的單向邊
表示在i號點買進,
再在第二層的每個點向第三層的對應點各連線一條權值為val[j]的有向邊
表示在j號點賣出,
構建好分層圖後
我們在分層圖上跑最長路
以第三層中的n號點為終點
便可求解
即使有負權,可我們因為跑的是最長路
所以dijistla不受影響
ac程式碼如下
時間複雜度邊為3mlog3m

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<queue>
#define inf -0x3f3f3f3f
using namespace std;
const int maxn=5e5;
inline int read(){
	int ret=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-f;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		ret=ret*10+(ch^'0');
		ch=getchar();
	}
	return f*ret;
}
struct edge{
	int nex;
	int to;
	int v;
}e[maxn*3];
struct node{
	int u,d;
	bool operator <(const node &x) const{
		return x.d<d;
	}
};

int head[maxn*3];
int cnt;
void add(int u,int to,int v){
	cnt++;
	e[cnt].nex=head[u];
	e[cnt].to=to;
	e[cnt].v=v;
	head[u]=cnt;
}
int n,m;
int dis[maxn*3];
int val[maxn];
inline void di(int s){
	for(int i=1;i<=n;i++){
		dis[i]=inf;
	}
	dis[s]=0;
	priority_queue<node>q;
	q.push((node){s,0});
	while(!q.empty()){
		node f=q.top();
		q.pop();
		int u=f.u;
		int d=f.d;
		if(dis[u]!=d)
			continue;
		for(int i=head[u];i;i=e[i].nex){
			int v=e[i].v;
			int y=e[i].to;
			if(dis[u]+v>dis[y]){
				dis[y]=dis[u]+v;
				q.push((node){y,dis[y]});
			}
		}
			
	}
}
int main(){
//	freopen("a.in","r",stdin);
	n=read();
	m=read();
	for(int i=1;i<=n;i++){
		val[i]=read();
		add(i,i+n,-val[i]);
		add(i+n,+2*n+i,val[i]);
	}
	int x,y,z;
	for(int i=1;i<=m;i++){
		x=read();
		y=read();
		z=read();
		add(x,y,0);
		add(x+n,y+n,0);
		add(x+2*n,y+2*n,0);
		if(z==2){
			add(y,x,0);
			add(y+n,x+n,0);
			add(y+2*n,x+2*n,0);
		}
	}
	n=n*3;
	di(1);
	cout<<dis[n];
	return 0;
}

結束嘍!