1. 程式人生 > 其它 >P2886 [USACO07NOV] Cow Relays G - 貪心

P2886 [USACO07NOV] Cow Relays G - 貪心

本文參考高逸涵的《部分貪心思想在資訊學競賽中的應用》。

一種 \(\mathcal{O}(T^2)\) 的做法

首先感性理解一下:當 \(N\) 很大的時候,最優策略一定是在某個邊權很小的邊上繞圈。


性質 1:只有可能在路徑上一條最短的邊上連續走多次。

證明:假如在其他邊上連續走了多次,那我們可以將路徑上來回走的一對邊 \((u\to v,v\to u)\) 刪去,換成在最短的邊上來回走,這樣一定不劣。


性質 2:只有路徑上的一條最短邊可能被在同一方向經過三次及以上,這裡“經過”不要求連續。

證明:假如另外一條邊被在同一方向經過三次及以上,那麼路徑上一定有包含這條邊的兩個環。若這兩個環中有至少一個長度為偶數,那麼可以刪去這個環並在最短邊上來回走;否則,兩個環的長度都是奇數,把兩個環都刪去,換成在最短邊上來回走即可。


結論:最優方案下,只會有至多一條邊被經過 \(>2\) 次。

\(f_{u,d}\) 表示從 \(S\)\(u\),經過的邊數為 \(d\) 的最短路,顯然可以 \(\mathcal{O}(T^2)\) 預處理。同理,預處理 \(g_{u,d}\) 表示從 \(E\) 的。

列舉每條邊作為那條被多次經過的邊,並 \(\mathcal{O}(T)\) 算出最小值即可。具體計算方式可見程式碼中的 Calc 函式。

程式碼

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
typedef long long ll;
const int N=1005,M=105,Inf=0x3f3f3f3f;
int n;
int Get(int u){
	static int num[N]{};
	if(!num[u]) return num[u]=++n;
	return num[u];
}
int t,m,S,T,f[M][M<<1],g[M][M<<1];
struct Edge{int u,v,w;}edge[M];
int Calc(int u,int len){
	int res=Inf;
	For(c,0,1){
		int mn=Inf,k=(t+c)%2-2;
		for(int j=m*2+c;j>=0;j-=2){
			while(k<m*2&&t-j-k>1){
				k+=2,mn+=2*len;
				mn=min(mn,g[u][k]);
			}
			if(mn>=Inf||f[u][j]>=Inf) continue;
			res=min(res,mn+f[u][j]+(t-j-k)*len);
		}
	}
	return res;
}
int main(){
	cin>>t>>m>>S>>T;
	S=Get(S),T=Get(T);
	For(i,1,m){
		cin>>edge[i].w>>edge[i].u>>edge[i].v;
		edge[i].u=Get(edge[i].u),edge[i].v=Get(edge[i].v);
	}
	memset(f,0x3f,sizeof f),memset(g,0x3f,sizeof g);
	f[S][0]=0,g[T][0]=0;
	For(i,1,m*2){
		For(j,1,m){
			f[edge[j].u][i]=min(f[edge[j].u][i],f[edge[j].v][i-1]+edge[j].w);
			f[edge[j].v][i]=min(f[edge[j].v][i],f[edge[j].u][i-1]+edge[j].w);
			g[edge[j].u][i]=min(g[edge[j].u][i],g[edge[j].v][i-1]+edge[j].w);
			g[edge[j].v][i]=min(g[edge[j].v][i],g[edge[j].u][i-1]+edge[j].w);
		}
	}
	int ans=Inf;
	For(i,1,m){
		ans=min({ans,Calc(edge[i].u,edge[i].w),Calc(edge[i].v,edge[i].w)});
	}
	cout<<ans;
	return 0;
}
Written by Alan_Zhao