【BZOJ4774/4006】修路/[JLOI2015]管道連接 斯坦納樹
阿新 • • 發佈:2017-11-26
|| spa 無向圖 rip sin esp cstring str microsoft
一行一個整數,表示答案,如果無解輸出-1
6 5 1
6 9 4
9 4 2
9 4 10
6 1 2
2 3 6
7 6 10
5 7 1
9 7 2
5 9 10
1 6 8
4 7 4
5 7 1
2 6 9
10 10 6
8 7 2
10 9 10
1 2 4
10 1 8
9 9 7
【BZOJ4774】修路
Description
村子間的小路年久失修,為了保障村子之間的往來,法珞決定帶領大家修路。對於邊帶權的無向圖 G = (V, E),請選擇一些邊,使得1 <= i <= d, i號節點和 n - i + 1 號節點可以通過選中的邊連通,最小化選中的所有邊的權值和。Input
第一行兩個整數 n, m,表示圖的點數和邊數。接下來的 m行,每行三個整數 ui, vi, wi,表示有一條 ui 與 vi 之間,權值為 wi 的無向邊。 1 <= d <= 4 2d <= n <= 10^4 0 <= m <= 10^4 1 <= ui, vi <= n 1 <= wi <= 1000Output
Sample Input
10 20 16 5 1
6 9 4
9 4 2
9 4 10
6 1 2
2 3 6
7 6 10
5 7 1
9 7 2
5 9 10
1 6 8
4 7 4
5 7 1
2 6 9
10 10 6
8 7 2
10 9 10
1 2 4
10 1 8
9 9 7
Sample Output
8題解:設f[S][i]表示已經連通的關鍵點狀態為S,當前位於點i的最小權值和。轉移就是斯坦納樹。
再設g[S]表示已經連通的關鍵點狀態為S的最小權值和。只有當S保證所有的關鍵點對的連通狀態相同時,才可以從f[S][..]更新到g[S],最後對g數組跑枚舉子集的DP即可。
P.S.我至今才會枚舉子集的正確姿勢~~~
#include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <utility> #define mp(A,B) make_pair(A,B) using namespace std; int n,m,d,cnt,now,tot; int f[1<<8][10010],to[20010],next[20010],val[20010],head[10010],vis[10010],p[20],Log[1<<8],ref[1<<8],g[1<<8]; priority_queue<pair<int,int> > q; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } inline void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } inline void dij(int S,int x) { q.push(mp(-f[S][x],x)); int i,u; now++; while(!q.empty()) { u=q.top().second,q.pop(); if(vis[u]==now) continue; vis[u]=now; for(i=head[u];i!=-1;i=next[i]) if(f[S][to[i]]>f[S][u]+val[i]) f[S][to[i]]=f[S][u]+val[i],q.push(mp(-f[S][to[i]],to[i])); } } int main() { int i,a,b,c,x,y; n=rd(),m=rd(),d=rd(); memset(head,-1,sizeof(head)); for(i=1;i<=m;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c); for(i=0;i<2*d;i++) Log[1<<i]=i; memset(f,0x3f,sizeof(f)),memset(g,0x3f,sizeof(g)); for(i=1;i<=d;i++) { f[1<<(i-1)][i]=0,dij(1<<(i-1),i); f[1<<(d+i-1)][n-i+1]=0,dij(1<<(d+i-1),n-i+1); } for(x=1;x<(1<<(2*d));x++) { for(tot=0,y=x;y;y-=y&-y) p[tot++]=y&-y; for(y=1;y<(1<<tot);y++) { ref[y]=ref[y^(y&-y)]|p[Log[y&-y]]; for(i=1;i<=n;i++) if(f[x][i]>f[ref[y]][i]+f[x^ref[y]][i]) f[x][i]=f[ref[y]][i]+f[x^ref[y]][i]; } for(i=1;i<=n;i++) dij(x,i); } for(x=1;x<(1<<d);x++) for(i=1;i<=n;i++) g[x]=min(g[x],f[(x<<d)|x][i]); for(x=1;x<(1<<d);x++) { for(tot=0,y=x;y;y-=y&-y) p[tot++]=y&-y; for(y=1;y<(1<<tot);y++) { ref[y]=ref[y^(y&-y)]|p[Log[y&-y]]; g[x]=min(g[x],g[ref[y]]+g[x^ref[y]]); } } if(g[(1<<d)-1]==0x3f3f3f3f) printf("-1"); else printf("%d",g[(1<<d)-1]); return 0; }
#include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <utility> #define mp(A,B) make_pair(A,B) using namespace std; int n,m,d,cnt,now; int to[6010],next[6010],val[6010],head[1010],f[1<<10][1010],g[1<<10],p[15],w[15],vis[1<<10]; priority_queue<pair<int,int> > q; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } inline void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } inline void dij(int S,int x) { q.push(mp(-f[S][x],x)); int i,u; now++; while(!q.empty()) { u=q.top().second,q.pop(); if(vis[u]==now) continue; vis[u]=now; for(i=head[u];i!=-1;i=next[i]) if(f[S][to[i]]>f[S][u]+val[i]) f[S][to[i]]=f[S][u]+val[i],q.push(mp(-f[S][to[i]],to[i])); } } int main() { n=rd(),m=rd(),d=rd(); int i,j,a,b,c,x,y; memset(head,-1,sizeof(head)); for(i=1;i<=m;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c); memset(f,0x3f,sizeof(f)),memset(g,0x3f,sizeof(g)); for(i=0;i<d;i++) w[i]=rd(),p[i]=rd(),f[1<<i][p[i]]=0,dij(1<<i,p[i]); for(x=1;x<(1<<d);x++) { for(y=(x-1)&x;y;y=(y-1)&x) for(i=1;i<=n;i++) f[x][i]=min(f[x][i],f[y][i]+f[x^y][i]); for(i=1;i<=n;i++) dij(x,i); } for(x=1;x<(1<<d);x++) for(i=1;i<=n;i++) g[x]=min(g[x],f[x][i]); for(x=1;x<(1<<d);x++) { for(i=0;i<d;i++) for(j=0;j<i;j++) if(w[i]==w[j]&&((x>>i)&1)!=((x>>j)&1)) g[x]=0x3f3f3f3f; } for(x=1;x<(1<<d);x++) for(y=(x-1)&x;y;y=(y-1)&x) g[x]=min(g[x],g[y]+g[x^y]); printf("%d",g[(1<<d)-1]); return 0; }
【BZOJ4774/4006】修路/[JLOI2015]管道連接 斯坦納樹