题:https://www.luogu.com.cn/problem/P6192
题意:求最小斯坦纳树
分析:答案一定是树,因为有环的话,可以删除成环边让答案更小;
dp[i][s],表示以 i 为根,状态为s的最小代价,枚举子集和补集来dp,用最短路把每个S松弛一下
#include<bits/stdc++.h> using namespace std; #define pb push_back #define MP make_pair #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r typedef long long ll; const int mod=1e9+7; const int M=1e4+6; const int inf=0x3f3f3f3f; const ll INF=1e18; int dp[102][M]; struct qnode{ int v; ll c; qnode(int _v=0,ll _c=0):v(_v),c(_c){} bool operator <(const qnode &r)const{ return c>r.c; } }; struct Edge{ int v; ll cost; Edge(int _v=0,ll _cost=0):v(_v),cost(_cost){} }; vector<Edge>E[M]; bool vis[M]; priority_queue<qnode>que; void Dij(int cursta){ memset(vis,false,sizeof(vis)); qnode tmp; while(!que.empty()){ tmp=que.top(); que.pop(); int u=tmp.v; if(vis[u])continue; vis[u]=true; for(int i=0;i<E[u].size();i++){ int v=E[tmp.v][i].v; int cost=E[u][i].cost; if(!vis[v]&&dp[v][cursta]>dp[u][cursta]+cost){ dp[v][cursta]=dp[u][cursta]+cost; que.push(qnode(v,dp[v][cursta])); } } } } void addedge(int u,int v,ll w){ E[u].push_back(Edge(v,w)); } int p[M]; int main(){ int n,m,k; scanf("%d%d%d",&n,&m,&k); for(int u,v,w,i=1;i<=m;i++){ scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } memset(dp,0x3f,sizeof(dp)); int limit=dp[0][0]; for(int i=1;i<=k;i++){ scanf("%d",&p[i]); dp[p[i]][1<<(i-1)]=0; } for(int s=1;s<(1<<k);s++){ for(int i=1;i<=n;i++){ for(int sta=s&(s-1);sta;sta=s&(sta-1))///枚举子集 dp[i][s]=min(dp[i][s],dp[i][sta]+dp[i][s^sta]); if(dp[i][s]<limit) que.push(qnode(i,dp[i][s])); } Dij(s); } printf("%d ",dp[p[1]][(1<<k)-1]);///因为是要连通起k个指定节点,所以每个dp[p[i]]都是最小的,所以随便取一个 return 0; }