题目描述:
给出一张图
给出若干个点和其频率,要求相同频率的点可以连通,求最小代价。
题目分析:
观察到带频率的点数很少,只有10个左右
那我们就可以做斯坦纳树
这些相同频率的点形成一个斯坦纳树,其实最后形成的是个斯坦纳树森林,所以最后要搞一下子集DP(雾
题目链接:
Ac 代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <vector>
const int maxm=6000;
std::vector <int> p[20];
std::queue <int> dl;
int inf;
int f[maxm][1<<10],g[1<<10],n,m,num;
int head[maxm],to[maxm<<1],net[maxm<<1],cost[maxm<<1],cnt;
bool vis[maxm];
int pos[20],w[2000];
int point[20];
void addedge(int u,int v,int c)
{
cnt++;
to[cnt]=v,cost[cnt]=c,net[cnt]=head[u],head[u]=cnt;
}
void spfa(int sta)
{
while(!dl.empty()) dl.pop();
for(int i=1;i<=n;i++)
if(f[i][sta]<inf) dl.push(i),vis[i]=1;
while(!dl.empty())
{
int x=dl.front();
dl.pop();
vis[x]=0;
for(int i=head[x];i;i=net[i])
if(f[to[i]][sta]>f[x][sta]+cost[i])
{
f[to[i]][sta]=f[x][sta]+cost[i];
if(!vis[to[i]]) vis[to[i]]=1,dl.push(to[i]);
}
}
}
bool check(int sta)
{
for(int i=0;i<num;i++)
if((sta>>i)&1)
{
int poi=pos[i+1];
for(int j=0;j<p[poi].size();j++)
if(!((1<<(p[poi][j]-1))&sta)) return 0;
}
return 1;
}
int main()
{
scanf("%d%d%d",&n,&m,&num);
for(int i=1;i<=m;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
addedge(u,v,c),addedge(v,u,c);
}
for(int i=1;i<=num;i++)
{
int dx,id;
scanf("%d%d",&dx,&id);
w[id]=i;
p[dx].push_back(i);
pos[i]=dx;
point[i]=id;
}
memset(f,127/3,sizeof(f)),memset(g,127/3,sizeof(g));
inf=f[0][0];
for(int i=1;i<=num;i++)
{
int nowd=point[i];
f[nowd][1<<(w[nowd]-1)]=0;
}
for(int s=0;s<(1<<num);s++)
{
for(int i=1;i<=n;i++)
for(int j=s&(s-1);j;j=(j-1)&s)
f[i][s]=std::min(f[i][s],f[i][j]+f[i][s-j]);
spfa(s);
for(int i=1;i<=n;i++) g[s]=std::min(g[s],f[i][s]);
}
for(int s=0;s<(1<<num);s++)
for(int i=s&(s-1);i;i=(i-1)&s)
if(check(i)&&check(s-i))
g[s]=std::min(g[s],g[i]+g[s-i]);
printf("%d\n",g[(1<<num)-1]);
return 0;
}

本文介绍了一种解决特定斯坦纳树问题的方法,该问题要求在考虑点频率的情况下找到连接特定节点集合的最小代价路径。文章提供了详细的算法实现,包括使用子集DP优化斯坦纳树森林的构建过程。
8051

被折叠的 条评论
为什么被折叠?



