codeforce 240E 最小樹形圖+路徑記錄更新
阿新 • • 發佈:2018-10-18
pri edge 沒有 sin 準備 main truct amp int
最小樹形圖的路徑是在不斷建立新圖的過程中更新的,因此需要開一個結構體cancle記錄那些被更新的邊,保存可能會被取消的邊和邊在舊圖中的id
在朱劉算法最後添加了一個從後往前遍歷新建邊的循環,這可以理解為回溯,通過cancle結構體不斷找到上一個時間點更新的邊id,並且取消那些被代替的邊
至於為什麽要按建圖時間從後往前回溯,我在下面舉了一個例子:
上圖取自朱劉算法標準示例,最小樹形圖路徑保存與更新
拿節點v3舉例
指向v3的邊有三條:a4,a13,a9
第一次循環:步驟1,2建立最短弧集:a4被保存在最短弧集E中,usedE[4]=1
步驟3:準備新建圖,此時a9,a13權值被更新,其舊id被保存在cancle.id中,a4的id被保存在cancle.pre中,假設a9,a13被賦予新id a16,a17
第二次循環:步驟1, 2建立新圖:a17被保存在新圖中,usedE[17]=1
步驟3:準備建立新圖,a16被更新,假設其被賦予新id a18
第三次循環:沒有環了,退出循環
退出循環後從後往前便利新建邊,依舊拿v3舉例
首先是循環到usedE[17]:cancle[17].id=13,因此usedE[13]=1
cancle[17].pre=4,因此usedE[4]=0
最後在遍歷被使用邊時,可以發現被使用的是邊a13,而a4被a13代替了
大家也可以拿其余點自己試試,下面貼上我的代碼,codeforce240E,輸入輸出有點坑,需要從通過文件io
#include<iostream> #include<cstring> #include<cstdio> #define MAXN 100005 #define MAXM MAXN*20 #define INF 0x3f3f3f3f using namespace std; struct Edge{ int u,v,cost; int w;//原始權值 int id; }edge[MAXM]; inline void addedge(int u,int v,int cost,int w,int id){ edge[id].cost=edge[id].w=cost; edge[id].u=u; edge[id].v=v; edge[id].id=id; } struct Cancle{// int pre;//保存可能被取消的那條邊的id int id;//保存可能新增的那條邊更新前的id }cancle[MAXM]; int pre[MAXN],id[MAXN],vis[MAXN],in[MAXN]; int preid[MAXN],usedE[MAXN]; int zhuliu(int root,int n,int m){ int res=0,total=m;//total是下一條新建邊的id while(1){ for(int i=0;i<n;i++) in[i]=INF; for(int i=0;i<m;i++) if(edge[i].u!=edge[i].v && edge[i].cost<in[edge[i].v]){ pre[edge[i].v]=edge[i].u; in[edge[i].v]=edge[i].cost; //更新被加入到邊集E的那條邊的id preid[edge[i].v]=edge[i].id; } for(int i=0;i<n;i++) if (i!=root && in[i]==INF) return -1; int tn=0; memset(id,-1,sizeof id); memset(vis,-1,sizeof vis); in[root]=0; for(int i=0;i<n;i++){ res+=in[i]; int v=i; ///將新圖中被使用到的邊保存 if(i!=root) usedE[preid[i]]++; while(v!=root && vis[v]!=i && id[v]==-1){ vis[v]=i; v=pre[v]; } if(v!=root && id[v]==-1){ for(int u=pre[v];u!=v;u=pre[u]) id[u]=tn; id[v]=tn++; } } if(tn==0) break; for(int i=0;i<n;i++) if(id[i]==-1) id[i]=tn++; for(int i=0;i<m;i++){ int v=edge[i].v; edge[i].u=id[edge[i].u]; edge[i].v=id[edge[i].v]; if(edge[i].u!=edge[i].v){ edge[i].cost-=in[v]; //把這條邊的更新信息保存一下 cancle[total].id=edge[i].id;//註意,這裏是保留該邊更新前的id! cancle[total].pre=preid[v];//原本指向v的邊被取消了 edge[i].id=total++; } } n=tn; root=id[root]; } /* 為什麽要從後往前? */ for(int i=total-1;i>=m;i--) if(usedE[i]){ usedE[cancle[i].pre]--; usedE[cancle[i].id]++; } return res; } int main(){ freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); int n,m; scanf("%d%d",&n,&m); int u,v,w; for(int i=0;i<m;i++){ scanf("%d%d%d",&u,&v,&w); u--,v--; addedge(u,v,w,w,i); } int root=0; int res=zhuliu(root,n,m); if(res==0||res==-1) printf("%d\n",res); else{ printf("%d\n",res); for(int i=0;i<m;i++) if(usedE[i]&&edge[i].w) printf("%d ",i+1); } printf("\n"); return 0; }
這段代碼掛在了test31.。不知道為什麽,望大佬指正,非常感謝!
codeforce 240E 最小樹形圖+路徑記錄更新