题目链接:点击打开链接
题目算是比较裸的斯坦纳树,求的是最小的斯坦纳森林,而且其中每颗树都要满足【代表家的点的点数要和代表破屋的点的点数一样多】。
首先先求一遍斯坦纳树,这个参考上一篇的斯坦纳树模板。
此处设stein[i][bitmask]为以i为根且包含点集中的bitmask状态的点的情况下的最小斯坦纳树。
其次,设dp[bitmask]为,在包含点集中的bitmask状态的点的情况下的最小满足条件的斯坦纳森林。
则dp[bitmask]有两种转移:
1. stein[i][bitmask],其中i为1到n的所有整数。
这一步其实就是求在bitmask状态下的最小斯坦纳树,将之前求的的最小斯坦纳树做个求最小值就行了。
2. dp[sub1]+dp[sub2],其中sub1和sub2为bitmask的子集,且sub1+sub2=bitmask,而且sub1和sub2要满足【代表家的点的点数要和代表破屋的点的点数一样多】这个条件。
这一步就是指按条件将这个森林劈成两个森林来dp然后进行合并。
注意无解的时候要输出-1。
代码如下:
#include<bits/stdc++.h>
using namespace std;
struct edge
{
int u,v,w;
edge(int uu=0,int vv=0,int ww=0):u(uu),v(vv),w(ww){}
};
struct SteinerTree
{
int n,k;
int dp[105][10005];
int st[105];
bool inque[105][10005];
queue<int> q;
vector<edge> e[105];
void init(int nn,int kk,vector<int> c)
{
n=nn;k=kk;
memset(dp,-1,sizeof(dp));
memset(st,0,sizeof(st));
for(int i=0;i<kk;i++)
st[c[i]]=(1<<i);
for(int i=1;i<=n;i++)
dp[i][st[i]]=0;
memset(inque,0,sizeof(inque));
while(!q.empty())q.pop();
for(int i=1;i<=n;i++)
e[i].clear();
}
void addedge(int u,int v,int w)
{
e[u].push_back(edge(u,v,w));
e[v].push_back(edge(v,u,w));
}
void SPFA(int b)
{
while(!q.empty())
{
int u=q.front();
q.pop();
inque[u][b]=false;
for(int i=0;i<e[u].size();i++)
{
int &v=e[u][i].v;
if(dp[v][st[v]|b]==-1||dp[v][st[v]|b]>dp[u][b]+e[u][i].w)
{
dp[v][st[v]|b]=dp[u][b]+e[u][i].w;
if((st[v]|b!=b)||inque[v][b])continue;
q.push(v);
inque[v][b]=true;
}
}
}
}
void BuildTree()
{
for(int j=1;j<(1<<k);j++)
{
for(int i=1;i<=n;i++)
{
if(st[i]&&(st[i]&j)==0)continue;
for(int sub=(j-1)&j;sub;sub=(sub-1)&j)
{
int x=st[i]|sub,y=st[i]|(j-sub);
if(dp[i][x]!=-1&&dp[i][y]!=-1)
{
if(dp[i][j]==-1||dp[i][j]>(dp[i][x]+dp[i][y]))
{
dp[i][j]=dp[i][x]+dp[i][y];
}
}
}
if(dp[i][j]!=-1)
{
q.push(i);
inque[i][j]=true;
}
}
SPFA(j);
}
}
}S;
int MinDP(int b)
{
int ret=S.dp[1][b];
for(int i=2;i<=S.n;i++)
{
if(S.dp[i][b]==-1)continue;
if(ret==-1||ret>S.dp[i][b])
ret=S.dp[i][b];
}
return ret;
}
int dp[10005];
int dfs(int b)
{
int k=S.k/2;
if(dp[b]!=-1)return dp[b];
dp[b]=MinDP(b);
for(int sub=(b-1)&b;sub;sub=(sub-1)&b)
{
int cnt1=0,cnt2=0,p=sub;
for(int i=0;i<k;i++)
{
if(p&1)cnt1++;
p>>=1;
}
for(int i=0;i<k;i++)
{
if(p&1)cnt2++;
p>>=1;
}
if(cnt1!=cnt2)continue;
if(!(dfs(sub)>=0&&dfs(b-sub)>=0))continue;
if(dp[b]==-1||dp[b]>dfs(sub)+dfs(b-sub))
dp[b]=dfs(sub)+dfs(b-sub);
}
return dp[b];
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
vector<int> V;
for(int i=1;i<=k;i++)
V.push_back(i);
for(int i=n-k+1;i<=n;i++)
V.push_back(i);
S.init(n,k*2,V);
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
S.addedge(u,v,w);
}
S.BuildTree();
// for(int i=1;i<=n;i++)
// for(int j=0;j<(1<<(k*2));j++)
// printf("dp[%d][%d]=%d\n",i,j,S.dp[i][j]);
memset(dp,-1,sizeof(dp));
int ans=dfs((1<<(k*2))-1);
if(ans!=-1)printf("%d\n",ans);
else printf("No solution\n");
}
}