1. 程式人生 > >BZOJ_5180_[Baltic2016]Cities_ 斯坦納樹

BZOJ_5180_[Baltic2016]Cities_ 斯坦納樹

spa pair inline 內部 ace make void spfa space

BZOJ_5180_[Baltic2016]Cities_ 斯坦納樹

題意:

給定n個點,m條雙向邊的圖。其中有k個點是重要的。每條邊都有一定的長度。 現在要你選定一些邊來構成一個圖,要使得k個重要的點相互連通,求邊的長度和的最小值。 分析: 斯坦納樹裸題 dis[i][j]表示關鍵點連通狀態為i,當前在點j的最小花費 有兩個轉移:內部枚舉子集,外部spfa轉移 這道題卡spfa,那我們用dij就好啦 代碼:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
#define N 100050
#define LL long long
priority_queue <pair <LL,int> >q;
int head[N],to[N<<2],nxt[N<<2],cnt;
int n,m,k,id[10],vis[33][N];
LL dis[33][N],val[N<<2];
inline void add(int u,int v,LL w){
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;val[cnt]=w;
}
int main(){
    scanf("%d%d%d",&n,&k,&m);
    int i,j,mask=(1<<k)-1,p,x,y;
    LL z;
    memset(dis,0x3f,sizeof(dis));
    for(i=1;i<=k;i++) scanf("%d",&id[i]);
    for(i=1;i<=k;i++) dis[1<<i-1][id[i]]=0;
    for(i=1;i<=m;i++) { scanf("%d%d%lld",&x,&y,&z);add(x,y,z);add(y,x,z); }
    for(j=1;j<=mask;j++){
        for(i=1;i<=n;i++){
            for(p=j&(j-1);p;p=j&(p-1)){
                dis[j][i]=min(dis[j][i],dis[p][i]+dis[j-p][i]);
            }
        }
        for(i=1;i<=n;i++){
            q.push(make_pair(-dis[j][i],i));
        }
        while(!q.empty()){
            x=q.top().second;q.pop();
            if(vis[j][x])continue;
            vis[j][x]=1;
            for(i=head[x];i;i=nxt[i]){
                if(dis[j][to[i]]>dis[j][x]+val[i]){
                    dis[j][to[i]]=dis[j][x]+val[i];
                    q.push(make_pair(-dis[j][to[i]],to[i]));
                }
            }
        }
    }
    LL ans=1ll<<60;
    for(i=1;i<=n;i++)ans=min(ans,dis[mask][i]);
    printf("%lld\n",ans);
}

BZOJ_5180_[Baltic2016]Cities_ 斯坦納樹