题解:P10601 [NWERC 2006] Ticket to Ride
如果本题要求那八个城市全部互相连通,那么就是最小斯坦纳树的模板了,但是题目中只需要四对城市两两之间连通即可。
可以先跑一遍最小斯坦纳树,此时就能得到所有含有顶点
接着就可以再进行一次状压 dp,设
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=105,K=10;
int dp[N][1<<K];
bool vis[N];
vector<PII>e[N];
map<string,int>mp;
int n;
void dij(int S){
memset(vis,0,sizeof(vis));
priority_queue<PII,vector<PII>,greater<PII>>q;
for(int i=1;i<=n;i++)if(dp[i][S]!=0x3f3f3f3f)q.push({dp[i][S],i});
while(!q.empty()){
int x=q.top().second;q.pop();
if(vis[x])continue;
vis[x]=1;
for(PII t:e[x]){
int y=t.first,w=t.second;
if(dp[y][S]>dp[x][S]+w){
dp[y][S]=dp[x][S]+w;
q.push({dp[y][S],y});
}
}
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int m;cin>>n>>m;int k=8;
for(int i=1;i<=n;i++){
string s;cin>>s;
mp[s]=i;
}
for(int i=1;i<=m;i++){
string u,v;int w;cin>>u>>v>>w;
e[mp[u]].push_back({mp[v],w});
e[mp[v]].push_back({mp[u],w});
}
string s1,s2,s3,s4,s5,s6,s7,s8;cin>>s1>>s2>>s3>>s4>>s5>>s6>>s7>>s8;
int p[9]={0,mp[s1],mp[s2],mp[s3],mp[s4],mp[s5],mp[s6],mp[s7],mp[s8]};
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=k;i++)dp[p[i]][1<<i-1]=0;
for(int S=0;S<(1<<k);S++){
for(int s=S;s;s=((s-1)&S)){
for(int i=1;i<=n;i++)dp[i][S]=min(dp[i][S],dp[i][s]+dp[i][S^s]);
}
dij(S);
}
int ans[16]={0};
//ans[S] 表示只让 S 中的这几对点互相连通的最小花费
//转移时枚举子集 s,ans[S]=min(ans[S],ans[s]+ans[S^s])
for(int S=1;S<16;S++){
int i=__lg(S&(-S))+1;
int x=0;for(int j=0;j<4;j++)if(S&(1<<j))x|=((1<<j*2)|(1<<j*2+1));
ans[S]=dp[p[i*2]][x];
for(int s=S;s;s=((s-1)&S)){
ans[S]=min(ans[S],ans[s]+ans[S^s]);
}
}
cout<<ans[15];
return 0;
}