最優貿易

最優貿易

NOIp 2009提升組第三題 最優貿易html

前言

一眼瞄到題面中的樣例圖示——圖論!node

再去讀題,想到了最短路暴力求解,根據數據範圍分析一下,拿\(50pts\)應該是沒問題的(雖然暴力的時間複雜度真的大的可怕,大概是\(O(n^3)\)c++

因此,下面會介紹個人暴力最短路版實現以及正解版實現(正解之一:分層圖)spa


暴力最短路

PS:你們能夠直接跳到後面看分層圖的講解qwq.net

怎麼用最短路來騙範圍小的暴力分呢?code

枚舉唄!咱們循環枚舉每一種珂行的買入賣出方案,而後比較出其中的最大值便可htm

其中可行的方案必須知足如下規則(設每一個點的價格爲\(c[i]\),買入點爲\(i\),賣出點爲\(j\)):blog

  1. 路徑必須是從起點\(1\)到終點\(n\)(其中可屢次通過任意點)即:\(1\)\(i\)必須連通,\(i\)\(j\)必須連通,\(j\)\(n\)必須連通get

  2. \(c[j]\)必須\(>\)\(c[i]\),由於目的是賺錢,因此確定低入高出啊!不然就不進行貿易了it

知足以上條件後咱們就能夠將\(c[j]-c[i]\)\(ans\)做比較,最後輸出保存了最大答案的\(ans\)便可(注意, \(1\)\(n\)這兩個點也可能做爲買入點或賣出點

這個思路仍是很容易就編出代碼的(Dijkstra和SPFA是同樣的\(50pts\)):

#include <bits/stdc++.h>
using namespace std;
queue<int> q;
int n,m,x,y,z,tot,ans,c[500010];
int dis[5201][5201],vis[500010],head[500010];

struct node {
	int to,net;
} e[500010];

inline void add(int u,int v) {
	e[++tot].to=v;
	e[tot].net=head[u];
	head[u]=tot;
}

inline void spfa(int s) {
	for(register int i=1;i<=n;i++) {
		vis[i]=0;
		dis[s][i]=20050206;
	}
	dis[s][s]=0;
	vis[s]=1;
	q.push(s);
	while(!q.empty()) {
		int x=q.front();
		q.pop();
		for(register int i=head[x];i;i=e[i].net) {
			int v=e[i].to;
			if(dis[s][v]>dis[s][x]+1) {
				dis[s][v]=dis[s][x]+1;
				if(!vis[v]) {
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
}

int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;i++) {
		scanf("%d",&c[i]);
	}
	for(register int i=1;i<=m;i++) {
		scanf("%d%d%d",&x,&y,&z);
		add(x,y);
		if(z==2) add(y,x);
	}
	for(register int i=1;i<=n;i++) {
		spfa(i);
	}
	for(register int i=1;i<=n;i++) {
		for(register int j=1;j<=n;j++) {
			if(dis[j][n]==20050206) continue;
			if(c[j]-c[i]<=ans||c[j]<=c[i]) continue;
			if(dis[1][i]!=20050206&&dis[i][j]!=20050206) {
				ans=c[j]-c[i];
			}
		}
	}
	printf("%d",ans);
	return 0;
}

分層圖(正解之一)

這題還能分層圖??!!

在看到了這篇題解的標題後,簡直是頓悟啊!若是對分層圖比較熟悉,題意轉換能力較好的OIer應該仍是想得出來(跟我沒啥關係...)

不瞭解分層圖爲什麼物的,能夠看個人這篇題解 (雖然講的是分層圖最短路,可是也能夠輔助瞭解分層圖qvq)

  • 來解釋一下爲何會想到分層圖

題目中明確表示貿易只會進行一次

那咱們將買入標記爲第一層與第二層連邊,邊權爲\(-c[i]\);將賣出標記爲第二層與第三層連邊,邊權爲\(c[i]\)。而後從起點\(1\)走到\(3*n\)的最長路就是咱們最終的答案!

(有點很差理解?那咱們來看看圖,這圖有點大..很差意思啊)

藍色的線表示的就是買入,綠色的線表示的就是賣出,旁邊的東西就是邊權

在每一層中,有邊相連的兩個點的邊權爲0就好,這樣不會影響結果

如今給出\(100pts\)的AC代碼qwq:

#include <bits/stdc++.h>
using namespace std;
int n,m,u,v,z,tot,c[300010];
int dis[300010],head[300010];
priority_queue<pair<int,int> > q;

struct node {
	int to,net,val;
} e[300010];

inline void add(int u,int v,int w) {
	e[++tot].to=v;
	e[tot].val=w;
	e[tot].net=head[u];
	head[u]=tot;
}

inline void dijkstra() {
	for(register int i=1;i<=3*n;i++) dis[i]=-20050206;  //注意是3*n而不僅是n! 
	dis[1]=0;
	q.push(make_pair(0,1));
	while(!q.empty()) {
		int x=q.top().second;
		int y=q.top().first;
		q.pop();
		if(dis[x]>y) continue;
		for(register int i=head[x];i;i=e[i].net) {
			int v=e[i].to;
			if(dis[v]<dis[x]+e[i].val) {  //取最長路 
				dis[v]=dis[x]+e[i].val;
				q.push(make_pair(dis[v],v));
			}
		}
	}
}

int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;i++) scanf("%d",&c[i]);
	for(register int i=1;i<=m;i++) {  //每一層中連邊 
		scanf("%d%d%d",&u,&v,&z);
		add(u,v,0);
		add(u+n,v+n,0);
		add(u+n+n,v+n+n,0);
		if(z==2) {  //雙向邊 
			add(v,u,0);
			add(v+n,u+n,0);
			add(v+n+n,u+n+n,0);
		}
	}
	for(register int i=1;i<=n;i++) {  //層與層之間連邊 
		add(i,i+n,-c[i]);
		add(i+n,i+n+n,c[i]);
	}
	dijkstra();
	printf("%d",max(0,dis[3*n]));
	return 0;
}

最後,若是有任何不懂或不對的地方,歡迎你們在底下留言,我會及時回覆,謝謝orz

相關文章
相關標籤/搜索