看到题面容易想到跟斯坦纳树有关。
那么我们不妨设 f ( i , s t a ) f(i,sta) f(i,sta) 表示根为 i i i,关键点的状压状态为 s t a sta sta 时的最小代价。
那么所有的 f ( i , s t a ) f(i,sta) f(i,sta) 我们都可以用斯坦纳树的模板求出来。
现在考虑如何达到题目的要求。
因为考虑到频道的数量也小于 10 10 10,所以考虑一下能不能也用状压解决。
设 g ( s t a ′ ) g(sta') g(sta′) 表示频道的状压状态为 s t a ′ sta' sta′ 时的最小代价。(如果 s t a ′ sta' sta′ 二进制下的第 k k k 位是 1 1 1 就说明第 k k k 个频道的关键点都已经联通了,否则不连通)。
然后设 s t a p ( i ) stap(i) stap(i) 表示第 i i i 个频道的所有关键点的状压状态(类似于 f ( i , s t a ) f(i,sta) f(i,sta) 中的 s t a sta sta)。
显然,这个东西可以在读入的时候预处理。
然后说一下我一开始的想法:
对于每一个频道 i i i,把 g ( 1 < < i ) g(1<<i) g(1<<i) 的值设为 min ( f ( j , s t a p i ) ) min(f(j,stap_i)) min(f(j,stapi))。然后其他的 g ( ) g() g() 都设为 inf inf inf。
然后枚举每一个 s t a ′ sta' sta′ 的状态,更新: g ( s t a ′ ) = min ( g ( s ) + g ( s t a ′ − s ) ) g(sta')=min(g(s)+g(sta'-s)) g(sta′)=min(g(s)+g(sta′−s))。(其中 s s s 是 s t a ′ sta' sta′ 的子集)
但是我发现了一个重要的问题:
比如说这个图中,所有节点都是关键节点,点 1 1 1、 4 4 4 是频道 1 1 1 的关键节点,点 2 2 2、 3 3 3 是频道 2 2 2 的关键节点。(点上的黑色数字代表点的编号,红色数字代表这个点所属的频道, 边上的数字代表这条边的权值)。
显然有 g ( ( 1 ) 2 ) = min f ( i , ( 1001 ) 2 ) = w 1 + w 2 + w 3 g((1)_2)=min f(i,(1001)_2)=w_1+w_2+w_3 g((1)2)=minf(i,(1001)2)=w1+w2+w3, g ( ( 10 ) 2 ) = min f ( i , ( 0110 ) 2 ) = w 2 g((10)_2)=min f(i,(0110)_2)=w_2 g((10)2)=minf(i,(0110)2)=w2。
那么会得出来: g ( ( 11 ) 2 ) = w 1 + 2 w 2 + w 3 g((11)_2)=w_1+2w_2+w_3 g((11)2)=w1+2w2+w3。
但显然 g ( ( 11 ) 2 ) g((11)_2) g((11)2) 应该等于 w 1 + w 2 + w 3 w_1+w_2+w_3 w1+w2+w3。
这时候就会发现这样 dp 会算重边。
如何解决?
我想到的解决办法:
我们一开始初始化 g ( ) g() g() 的时候,本来是只初始化所有的 g ( 1 < < i ) g(1<<i) g(1<<i),而我们现在把 g ( ) g() g() 的每一个状态都初始化。
意思是说,对于每一个 s t a ′ sta' sta′,我们都用 f ( ) f() f() 对 g ( s t a ′ ) g(sta') g(sta′) 进行初始化。
具体过程可以看代码,用 dfs 实现。
最后再按原来的方法,更新: g ( s t a ′ ) = min ( g ( s ) + g ( s t a ′ − s ) ) g(sta')=min(g(s)+g(sta'-s)) g(sta′)=min(g(s)+g(sta′−s))。(其中 s s s 是 s t a ′ sta' sta′ 的子集)
代码如下:
#include<bits/stdc++.h>
#define N 1010
#define M 3010
using namespace std;
int n,m,p,anssta,id[15],num[15],stap[15];
int cnt,head[N],w[M<<1],to[M<<1],nxt[M<<1];
int f[N][1025],g[1025];
bool inq[N];
//anssta是答案的状压状态(形如sta')
//stap是每一个频道对应的状压状态(形如sta)
queue<int>q;
void adde(int u,int v,int wi)
{
to[++cnt]=v;
w[cnt]=wi;
nxt[cnt]=head[u];
head[u]=cnt;
}
void spfa(int sta)
{
while(!q.empty())
{
int u=q.front();
q.pop();
inq[u]=false;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(f[u][sta]+w[i]<f[v][sta])
{
f[v][sta]=f[u][sta]+w[i];
if(!inq[v])
{
inq[v]=true;
q.push(v);
}
}
}
}
}
void dfs(int k,int sum,int sump)
{
if(k==p+1)
{
for(int i=1;i<=n;i++)
g[sump]=min(g[sump],f[i][sum]);
return;
}
dfs(k+1,sum|stap[k],sump|(1<<(k-1)));//枚举选这个频道(sta'的第k位是1)
dfs(k+1,sum,sump);//不选这个频道(sta'的第k位是0)
}
int main()
{
memset(f,0x3f,sizeof(f));
memset(g,0x3f,sizeof(g));
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
adde(u,v,w),adde(v,u,w);
}
for(int i=1;i<=p;i++)
{
scanf("%d%d",&id[i],&num[i]);
f[num[i]][1<<(i-1)]=0;
stap[id[i]]|=(1<<(i-1));
anssta|=(1<<(id[i]-1));
}
int maxn=(1<<p)-1;
for(int sta=1;sta<=maxn;sta++)
{
for(int i=1;i<=n;i++)
{
for(int now=sta;now;now=sta&(now-1))
f[i][sta]=min(f[i][sta],f[i][now]+f[i][sta-now]);
if(f[i][sta]!=0x3f3f3f3f)
{
q.push(i);
inq[i]=true;
}
}
spfa(sta);
}
//以上是模板斯坦纳树
dfs(1,0,0);
for(int sta=1;sta<=anssta;sta++)
for(int now=sta;now;now=sta&(now-1))
g[sta]=min(g[sta],g[now]+g[sta-now]);
printf("%d
",g[anssta]);
return 0;
}