[JLOI2015]管道连接
题目传送门
分析:
同色点连通挺恶心的
要求全部点连通的话就可以直接斯坦纳树了
同色点连通满足还要考虑异色点可能会共用边使答案更小
于是尝试枚举一个颜色集合的点全部连通,形成斯坦纳树
然后把所有集合拼起来,形成斯坦纳森林
之中有一种方案一定是最小的
枚举子集的子集的子集复杂度是\(O(4^n)\)
总复杂度为\(O(n4^p)\)
开O2跑得过还真离谱
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<map>
#define maxn 1005
#define maxm 6005
#define INF 0x3f3f3f3f
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,K,m;
int fir[maxn],nxt[maxm],to[maxm],len[maxm],cnt;
long long f[maxn][1<<10],g[1<<10];
bool vis[maxn];
map<int,int>M;
int cur;
int c[maxn],id[maxn];
struct node{
int u;long long w;
friend bool operator <(node x,node y){return x.w>y.w;}
};
priority_queue<node>Q;
inline void newnode(int u,int v,int w)
{to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt,len[cnt]=w;}
inline void dij(int S)
{
memset(vis,0,sizeof vis);
while(!Q.empty())
{
int u=Q.top().u;Q.pop();
if(vis[u])continue;vis[u]=1;
for(int i=fir[u];i;i=nxt[i])
if(f[to[i]][S]>f[u][S]+len[i])
Q.push((node){to[i],f[to[i]][S]=f[u][S]+len[i]});
}
}
inline long long solve(int t)
{
for(int i=0;i<(1<<t);i++)
{
for(int k=1;k<=n;k++)
{
for(int j=i&(i-1);j;j=i&(j-1))
f[k][i]=min(f[k][i],f[k][j]+f[k][i^j]);
if(f[k][i]!=f[0][0])Q.push((node){k,f[k][i]});
}
dij(i);
}
long long ans=1ll<<60;
for(int i=1;i<=n;i++)ans=min(ans,f[i][(1<<t)-1]);
return ans;
}
int main()
{
n=getint(),m=getint(),K=getint();
memset(f,INF,sizeof f);
for(int i=1;i<=m;i++)
{
int u=getint(),v=getint(),w=getint();
newnode(u,v,w),newnode(v,u,w);
}
for(int i=1;i<=K;i++)
{
c[i]=getint(),id[i]=getint();
if(!M[c[i]])M[c[i]]=++cur;
c[i]=M[c[i]];
}
memset(g,INF,sizeof g);
for(int i=1;i<(1<<cur);i++)
{
int tmp=0;
memset(f,INF,sizeof f);
for(int j=1;j<=K;j++)if(i&(1<<(c[j]-1)))f[id[j]][1<<tmp]=0,tmp++;
g[i]=solve(tmp);
}
for(int i=1;i<(1<<cur);i++)for(int j=i&(i-1);j;j=i&(j-1))
g[i]=min(g[i],g[j]+g[i^j]);
printf("%lld\n",g[(1<<cur)-1]);
}