描述
C 國有 n 個大城市和 m 條道路,每條道路串連這 n 個城市中的某兩個城市。任意兩個
城市之間最多隻有一條道路直接相連。這 m 條道路中有一部分為單向通行的道路,一部分
為雙向通行的道路,雙向通行的道路在統計條數時也計為 1 條。
C 國幅員遼闊,各地的資源分布情況各不相同,這就導致了同一種商品在不同城市的價
格不一定相同。但是,同一種商品在同一個城市的買入價和賣出價始終是相同的。
商人阿龍來到 C 國旅遊。當他得知同一種商品在不同城市的價格可能會不同這一資訊
之後,便決定在旅遊的同時,利用商品在不同城市中的價差賺回一點旅費。設 C 國 n 個城
市的標號從 1~ n,阿龍決定從 1 號城市出發,並最終在 n 號城市結束自己的旅行。在旅遊的
過程中,任何城市可以重複經過多次,但不要求經過所有 n 個城市。阿龍通過這樣的貿易方
式賺取旅費:他會選擇一個經過的城市買入他最喜歡的商品——水晶球,並在之後經過的另
一個城市賣出這個水晶球,用賺取的價差當做旅費。由於阿龍主要是來 C 國旅遊,他決定
這個貿易只進行最多一次,當然,在賺不到價差的情況下他就無需進行貿易。
假設 C 國有 5 個大城市,城市的編號和道路串連情況如下圖,單向箭頭表示這條道路
為單向通行,雙向箭頭表示這條道路為雙向通行。
假設 1~n 號城市的水晶球價格分別為 4,3,5,6,1。
阿龍可以選擇如下一條線路:1->2->3->5,並在 2 號城市以 3 的價格買入水晶球,在 3
號城市以 5的價格賣出水晶球,賺取的旅費數為 2。
阿龍也可以選擇如下一條線路 1->4->5->4->5,並在第 1 次到達 5 號城市時以 1 的價格
買入水晶球,在第 2 次到達 4 號城市時以 6 的價格賣出水晶球,賺取的旅費數為 5。
現在給出 n個城市的水晶球價格,m條道路的資訊(每條道路所串連的兩個城市的編號
以及該條道路的通行情況) 。請你告訴阿龍,他最多能賺取多少旅費。 格式 輸入格式
第一行包含 2 個正整數 n 和 m,中間用一個空格隔開,分別表示城市的數目和道路的
數目。
第二行 n 個正整數,每兩個整數之間用一個空格隔開,按標號順序分別表示這 n 個城
市的商品價格。
接下來 m行, 每行有 3 個正整數, x, y, z, 每兩個整數之間用一個空格隔開。 如果 z=1,
表示這條道路是城市 x到城市 y之間的單向道路;如果 z=2,表示這條道路為城市 x 和城市
y之間的雙向道路。 輸出格式
輸出共1 行, 包含 1 個整數, 表示最多能賺取的旅費。 如果沒有進行貿易,
則輸出 0。 範例 範例輸入
5 5 4 3 5 6 1 1 2 1 1 4 1 2 3 2 3 5 1 4 5 2
範例輸出
5
限制
每個測試點1s 來源
NOIP 2009
想著學學Tarjan吧 於是寫了個縮點+DP
然後喜聞樂見的爆0了
哈哈哈哈哈哈哈哈
不作死就不會死。。。
tarjan寫的沒錯 錯在dp了
一開始喪心病狂的從1遞推 每一個點取最大值
後來發現必須停到n
也就是說必須做完交易後能走到n
也就是說 連反向邊 可以從n走到交易點
所以就倒著DP啦。。。。
#include<iostream>#include<cstdio>#include<cstring>#include<stack>#include<cstdlib>#include<algorithm>using namespace std;const int lim=100011;struct self{int x,y;}s[lim*10],k[lim*10];int firsts[lim*10],nxts[lim*10];int firstk[lim*10],nxtk[lim*10];int ss,kk;int w[lim];int scc[lim],pre[lim],low[lim],maxv[lim],minv[lim];int m,n,a,b,c,sccnt,dfstime,x,y,z;stack<int>S;void tarjan(int i){ dfstime++; pre[i]=low[i]=dfstime; S.push(i); for(int e=firsts[i];e!=-1;e=nxts[e]) { if(!pre[s[e].y]) { tarjan(s[e].y); low[i]=min(low[i],low[s[e].y]); } else if(!scc[s[e].y]) low[i]=min(low[i],pre[s[e].y]); } if(low[i]==pre[i]) { sccnt++; for(;;) { int x=S.top();S.pop(); scc[x]=sccnt; maxv[sccnt]=max(w[x],maxv[sccnt]); if(minv[sccnt]==0)minv[sccnt]=w[x];else minv[sccnt]=min(w[x],minv[sccnt]); if(x==i)break; } }}void makesides(int x,int y){ ss++;s[ss].x=x;s[ss].y=y; nxts[ss]=firsts[x]; firsts[x]=ss;}void makesidek(int x,int y){ kk++;k[kk].x=x;k[kk].y=y; nxtk[kk]=firstk[x]; firstk[x]=kk;}int f[lim],ans;int findmin(int i){ if(f[i]!=-1)return f[i]; f[i]=minv[i]; for(int e=firstk[i];e!=-1;e=nxtk[e]) f[i]=min(f[i],findmin(k[e].y)); ans=max(ans,maxv[i]-f[i]); return f[i];}int main(){ memset(firsts,-1,sizeof(firsts)); memset(nxts,-1,sizeof(nxts)); memset(firstk,-1,sizeof(firstk)); memset(nxtk,-1,sizeof(nxtk)); scanf("%d%d",&m,&c); for(a=1;a<=m;a++)scanf("%d",&w[a]); for(a=1;a<=c;a++) { scanf("%d%d%d",&x,&y,&z); makesides(x,y); if(z==2)makesides(y,x); } for(a=1;a<=m;a++)if(!pre[a])tarjan(a); for(a=1;a<=m;a++) for(int e=firsts[a];e!=-1;e=nxts[e]) if(scc[a]!=scc[s[e].y]) makesidek(scc[s[e].y],scc[a]); memset(f,-1,sizeof(f)); findmin(scc[m]); cout<<ans<<endl; return 0;}
然後說下喜聞樂見的SPFA做法
(借用spfa的思想 更新走到每個點的最值)
Spfa可以一遍找到每個點的最大最小值
不過這個是有錯誤的吧 因為最後要走到n
可以從1走到i交易 但是可能從i走不到n 這時候就喜聞樂見了
不過由於資料弱 (-.-) 居然可以AC。。。
還有一個是兩次spfa的
第一次從1走找最小值
第二次從n走找最大值
這個應該是不存在什麼問題的吧
然後我嘗試寫了一下
感覺寫起來還不錯。
#include<iostream>#include<cstdio>#include<cstring>#include<stack>#include<cstdlib>#include<algorithm>#include<queue>using namespace std;const int lim=100011;const int inf=999999999;struct self{int x,y;}s[lim*10],k[lim*10];int firsts[lim*10],nxts[lim*10];int firstk[lim*10],nxtk[lim*10];int ss,kk;int w[lim];int maxv[lim],minv[lim];int m,n,a,b,c,x,y,z,ans;void makesides(int x,int y){ ss++;s[ss].x=x;s[ss].y=y; nxts[ss]=firsts[x]; firsts[x]=ss;}void makesidek(int x,int y){ kk++;k[kk].x=x;k[kk].y=y; nxtk[kk]=firstk[x]; firstk[x]=kk;}queue<int>q;bool inq[lim];void spfamin(){ int a; memset(inq,0,sizeof(inq)); q.push(1);inq[1]=1; for(a=1;a<=m;a++)minv[a]=inf; minv[1]=w[1]; while(!q.empty()) { int u=q.front();q.pop(); for(int e=firsts[u];e!=-1;e=nxts[e]) if(minv[s[e].y]>minv[u]) { minv[s[e].y]=min(minv[u],w[s[e].y]); if(!inq[s[e].y]) { inq[s[e].y]=1; q.push(s[e].y); } } }}void spfamax(){ int a; memset(inq,0,sizeof(inq)); q.push(m);inq[m]=1; for(a=1;a<=m;a++)maxv[a]=-inf; maxv[m]=w[m]; while(!q.empty()) { int u=q.front();q.pop(); for(int e=firstk[u];e!=-1;e=nxtk[e]) if(maxv[k[e].y]<maxv[u]) { maxv[k[e].y]=max(maxv[u],w[k[e].y]); if(!inq[k[e].y]) { inq[k[e].y]=1; q.push(k[e].y); } } }}int main(){ memset(firsts,-1,sizeof(firsts)); memset(nxts,-1,sizeof(nxts)); memset(firstk,-1,sizeof(firstk)); memset(nxtk,-1,sizeof(nxtk)); scanf("%d%d",&m,&c); for(a=1;a<=m;a++)scanf("%d",&w[a]); for(a=1;a<=c;a++) { scanf("%d%d%d",&x,&y,&z); makesides(x,y); makesidek(y,x); if(z==2) { makesides(y,x); makesidek(x,y); } } spfamin(); spfamax(); for(a=1;a<=m;a++)ans=max(ans,maxv[a]-minv[a]); cout<<ans<<endl; return 0;}