题解 P1841 【[JSOI2007]重要的城市】bitset题解
我的blog传送门:wjyyy
因为Floyd是一种玄学DP思想,所以它的状态更新来源有很多,在这个题里需要整理出它的阶段性与转移,看上去十分麻烦。而我们如果把Floyd当作最短路算法中的松弛,就是相当于在把两段最短路拼接在一起,拥有它们合在一起的性质。
重要城市
重要城市就是如果这个点被删掉,那么最短路的长度就会改变。因此这个点一定在最短路上。而当两点间的最短路有多条时,它们上的点不一定都是重要城市,经过分析我们可以这样理解:设
因此我们可以开一个三维数组
在上图中,1→8最短路计数为3,其中除了起点和终点,被经过了3次的点的点有2和7,因此它们是这条路径上的重要城市。
因为floyd的时间复杂度为
bitset优化
我们在上面提到,状态合并/更新需要额外枚举一个
这时可以考虑
我们可以换一个方式想想,如果这两个点之间已经找到了4条最短路,其中有3条经过点
因此
在一开始初始化时,把两个连接在一起的点上的关键城市设为两个端点,在floyd“松弛”最短路时,直接把两段最短路的关键城市“拼起来”,就是新的最短路上的关键城市。最后判断用
因此这道题的总复杂度为
Code:
#include<cstdio>
#include<cstring>
#include<bitset>
using std::bitset;
bitset<210> im[210][210];
int f[210][210];
int is[210];
int main()
{
memset(f,0x3f,sizeof(f));
int u,v,n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
f[i][i]=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
scanf("%d",&f[u][v]);
f[v][u]=f[u][v];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
im[i][j][i]=1;//初始化设两端为重要城市
im[i][j][j]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(f[i][k]+f[k][j]==f[i][j])
im[i][j]&=(im[i][k]|im[k][j]);//当更新计数时取交集
else if(f[i][k]+f[k][j]<f[i][j])//当更新最短路时直接赋值为两段的并集
{
f[i][j]=f[i][k]+f[k][j];
im[i][j]=im[i][k]|im[k][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
if(k!=i&&k!=j)//注意特判
if(im[i][j][k])
is[k]=1;
int flag=0;
for(int i=1;i<=n;i++)
if(is[i])
{
flag=1;
printf("%d ",i);
}
if(!flag)//注意判断无解
puts("No important cities.");
return 0;
}