2018 icpc-南京網路賽
阿新 • • 發佈:2018-11-11
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; }