题解:
由于$p<=10$,我们可以暴力枚举覆盖频道状态为$s$的特殊点,每次跑一遍斯坦纳树,
最后合并,方程和斯坦纳树的状态合并方程几乎一样。
代码:
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 1050 #define M 3050 #define ll long long const ll Inf = 0x3f3f3f3f3f3f3f3fll; inline int rd() { int f=1,c=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();} return f*c; } int n,m,p,q,hed[N],cnt; struct EG { int to,nxt,w; }e[2*M]; void ae(int f,int t,int w) { e[++cnt].to = t; e[cnt].nxt = hed[f]; e[cnt].w = w; hed[f] = cnt; } int ve[12][12],ct[12]; ll tmp[N][1<<10],g[1<<10]; bool vis[N]; queue<int>que; void spfa(int s) { while(!que.empty()) { int u = que.front();que.pop(); for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(tmp[to][s]>tmp[u][s]+e[j].w) { tmp[to][s]=tmp[u][s]+e[j].w; if(!vis[to])vis[to]=1,que.push(to); } } vis[u]=0; } } void sol(int k) { memset(tmp,0x3f,sizeof(tmp)); int now = 0; for(int i=0;i<q;i++) if(k&(1<<i)) for(int j=0;j<ct[i+1];j++) tmp[ve[i+1][j]][1<<now]=0,now++; for(int s=1;s<(1<<now);s++) { for(int i=1;i<=n;i++) { for(int t=s&(s-1);t;t=s&(t-1)) { tmp[i][s]=min(tmp[i][s],tmp[i][t]+tmp[i][s^t]); } if(tmp[i][s]!=Inf)que.push(i),vis[i]=1; } spfa(s); } for(int i=1;i<=n;i++) if(tmp[i][(1<<now)-1]<g[k]) g[k]=tmp[i][(1<<now)-1]; } int main() { n = rd(),m = rd(),p = rd(); for(int f,t,w,i=1;i<=m;i++) { f=rd(),t=rd(),w=rd(); ae(f,t,w),ae(t,f,w); } for(int f,t,i=1;i<=p;i++) { f=rd(),t=rd(); ve[f][ct[f]]=t;q+=(!ct[f]); ct[f]++; } memset(g,0x3f,sizeof(g)); for(int s=1;s<(1<<q);s++) { sol(s); for(int t=s&(s-1);t;t=s&(t-1)) g[s]=min(g[s],g[t]+g[s^t]); } printf("%lld ",g[(1<<q)-1]); return 0; }