1. 程式人生 > >2018 icpc-南京網路賽

2018 icpc-南京網路賽

L . Magical Girl Haze  題目連結:  https://nanti.jisuanke.com/t/31001

題解:分層圖-最短路(拆點建圖),這篇部落格寫的很詳細,包括整個思考的過程----https://www.cnblogs.com/shzr/p/9211128.html

1、將每個點拆成 k+1 個點建立分層圖,相當於將原圖複製k份,對於第 i 個點,拆成 i,i+n,i+2*n....i+k*n

2、若原圖中 i~j有一條權值為 x 的邊,則建圖如下

add_edge(i,j,x),add_edge(i+n,j+n,x).....add_edge(i+k*n,j+k*n,x)

add_edge(i,j+n,0),add_edge(i+n,j+2*n,0)....add_edge(i+(k-1)*n,j+k*n,0)

3、每向上走一層相當於走了一條權值為0的邊,假設走了(i,j+n,0)相當於從第0層走到了第一層,而把i~j這條邊的權值改為0。

5、最後輸出1~(k+1)*n的最短路即可,至多使得 k 條邊為0 ,最優解肯定為選擇 k 條邊為 0,ans = dis[k*n+n]。

注意:此題卡SPFA、vector鄰接表+堆優化的dijstra

AC程式碼:

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn = 1400010;
const int maxm = 5000010;
const ll inf = 0x3f3f3f3f;

int n,m,k; 
ll u[maxm],v[maxm],w[maxm],dis[maxn],first[maxm],NEXT[maxm];
bool vis[maxn];
//用陣列模擬鄰接表 
//first陣列用來儲存每個頂點其中一條邊的編號i,一遍列舉所有的邊 
//NEXT陣列用來儲存 "編號為i的邊" 的 "前一條邊" 的編號 

void init()
{
	for(int i = 1; i <= n+n*k; i++)
	{
		dis[i] = inf;
		vis[i] = false;
	}
	memset(first,-1,sizeof(first));
}

void cre_graph()
{
	int num = m+1;
	for(int i = 1; i <= m; i++)
	{
//		cin>>u[i]>>v[i]>>w[i];
		scanf("%lld%lld%lld",&u[i],&v[i],&w[i]);
		NEXT[i] = first[u[i]];//記錄某個頂點所訪問到的 "前一條邊" 的編號 
		first[u[i]] = i;//不斷的覆蓋某個頂點所訪問到的 "當前的邊" 的編號 
 		for(int j = 1; j <= k; j++)
 		{
 			u[num] = j*n+u[i];		
			v[num] = j*n+v[i];		
			w[num] = w[i];
			NEXT[num] = first[u[num]];//記錄某個頂點所訪問到的 "前一條邊" 的編號 
			first[u[num]] = num;//不斷的覆蓋某個頂點所訪問到的 "當前的邊" 的編號 
			num++;
			u[num] = (j-1)*n + u[i];
			v[num] = j*n + v[i];
			w[num] = 0;
			NEXT[num] = first[u[num]];//記錄某個頂點所訪問到的 "前一條邊" 的編號 
			first[u[num]] = num;//不斷的覆蓋某個頂點所訪問到的 "當前的邊" 的編號
			num++;
		}
	 }
}

void SPFA(int s)
{
	dis[s] = 0;
	queue<int> q;
	q.push(s);
	while(!q.empty())
	{
		int now = q.front();
		q.pop();
		for(ll i = first[now]; i != -1; i = NEXT[i])
		{
			ll to = v[i];
			if(dis[to] > w[i] + dis[now])
			{
				dis[to] = w[i] + dis[now];
				q.push(to);
			}
		}
	} 
}

int main()
{
//	ios::sync_with_stdio(false);
	int T;
//	cin>>T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d",&n,&m,&k);
//		cin>>n>>m>>k;
		if(m <= k)
			printf("0\n");
		else
		{
			init();
			cre_graph();
			int s = 1,e = n;
		 	SPFA(s);
//		 	ll ans = dis[e];
//		 	for(int i = 0; i <= k; i++)
//		 		ans = min(ans, dis[i*n+n]);
//		 	printf("%d\n",ans);
			//至多使得k條邊為0,最優的情況一定是選擇k條邊置為0 
			printf("%d\n",dis[k*n+n]);
		}
	} 
	return 0;
}