题解:P10339 [THUSC 2019] 补给计划
首先容易想到先用 floyd 预处理出任意两点之间的最短路,像这样:
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
当然输入的时候需要初始化一下:
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&l);
dis[u][v]=dis[v][u]=min(dis[u][v],(ll)l);
}
接着我们发现 \(K\) 的范围很小,可以考虑使用状压 dp 来实现。
- 状态定义
定义 \(f_{i,s}\) 表示前 \(i\) 座城市(编号为 \(1\) 到 \(i\)),覆盖住受灾城市集合为 \(S\),所需要的建设的最少补给站数量。 - 状态转移
- 被动转移
\[f_{i,s}=\min(f_{i-1,S},f_{i-1,S-S'}+1),
\]
其中 \(S'\) 表示第 \(i\) 号城市可以覆盖住的受灾城市集合。
不对,因为受灾城市是可以被多个补给站覆盖的。被第 \(i\) 号城市建设的补给站覆盖不代表就不会被前面城市建设的补给站覆盖。
因此考虑主动转移。
- 主动转移
\[f_{i+1,S|S'}=\min(f_{i+1,S|S'},f_{i,S}+1)
\]
其中 \(S'\) 为 \(i+1\) 号城市建设补给站能覆盖住的受灾城市集合。
我们可以这样处理集合 \(S'\):
遍历所有受灾城市,对于每座受灾城市检查能否被 \(i\) 覆盖到,若能则加入集合 \(S'\)。
代码如下:
int lim=(1<<k)-1;
f[0]=0;
for(int i=1;i<=n;++i){
if(!ok[i])continue;
int sta=0;
for(int j=1;j<=k;j++){
t=city[j];
if(dis[1][i]+dis[i][t]==dis[1][t])sta|=(1<<(j-1));//当前点被覆盖了
}
for(int j=0;j<=lim;++j)f[j|sta]=min(f[j|sta],f[j]+1);
}
- 时间复杂度
\(\mathcal{O}(n^3+n\times 2^k)\),可以通过此题。

浙公网安备 33010602011771号