写在前面
这道题也是我提交了好几遍的题,并不是说它有多么多么的重要,而是自己一直都会出错,所以为了使得自己不出错,就写了好几遍。
但是天天写其实也一点意义都没有,应该向焦老师(我的信奥老师)说的那样,做的题,今天写了之后,明天再写一遍,之后三天写一遍,再之后五天写一遍,再之后一个月写一遍。
题目描述
题目描述
参加jsoi冬令营的同学最近发现,由于南航校内修路截断了原来通向计算中心的路,导致去的路程比原先增加了近一公里。而食堂门前施工虽然也截断了原来通向计算中心的路,却没有使路程增加,因为可以找到同样长度的路作替代。其实,问题的关键在于,路截断的地方是交通要点。
同样的情况也出现在城市间的交通中。某些城市如果出了问题,可能会引起其他很多城市的交通不便。另一些城市则影响不到别的城市的交通。jsoi冬令营的同学发现这是一个有趣的问题,于是决定研究这个问题。
他们认为这样的城市是重要的:如果一个城市c被破坏后,存在两个不同的城市a和b(a, b均不等于c),a到b的最短距离增长了(或不通),则城市c是重要的。
jsoi冬令营的同学面对着一张教练组交给他们的城市间交通图,他们希望能找出所有重要的城市。现在就请你来解决这个问题。
输入输出格式
输入格式
第一行两个整数N,M,N为城市数,M为道路数
接下来M行,每行三个整数,表示两个城市之间的无向边,以及之间的路的长度
输出格式
一行,按递增次序输出若干的数,表示重要的城市。
输入输出样例
输入样例
#1
4 4
1 2 1
2 3 1
4 1 2
4 3 2
输出样例
#1
2
说明
30%的数据: N ≤ 20 N\le 20 N≤20;
60%的数据: N ≤ 100 N\le 100 N≤100;
100%的数据: N ≤ 200 , M ≤ N × ( N − 1 ) 2 , 0 < c ≤ 10000 N\le 200,M\le \frac{N\times (N-1)}{2},0<c\le 10000 N≤200,M≤2N×(N−1),0<c≤10000。c即路的长度。
保证不出现重边和自环,如果没有点的话需要输出一行 “No important cities.” 去掉引号 。
思路
因为这道题是在社交网络之后写的吧,所以我就想到了 F l o y d Floyd Floyd的最短路计数。
- 思路1:如果存在一个点 k k k经过了一个点对 ( i , j ) (i,j) (i,j)的所有的最短路,也就是说 k k k是 i i i到达 j j j的最短路的必经之点,这样子的话也就是说k是重要的城市;
- 思路2:我们可以用一个数组在 F l o y d Floyd Floyd更新的时候保留点对 ( i , j ) (i,j) (i,j)之间的最短路必经的点 k k k,之后再把保留的 k k k进行顺序上的处理,这样子的话,也是可以得到答案的。
对于上面的两个思路,可能有人会提出要 h a c k hack hack掉第二个方案,我们可以思考一下第二个方案的正确性。
如果对于 F l o y d Floyd Floyd的性质不了解的可以到社交网络这里看一波。
我们可以举一个例子:
s
−
>
i
−
>
j
−
>
t
s->i->j->t
s−>i−>j−>t在这一条路径上,可能有些人(没错,我就是有些人 )会提出说,第二种方案可能在点对
(
s
,
t
)
(s,t)
(s,t)会漏掉一个点。
但是在考虑一下 F l o y d Floyd Floyd的性质,由于 F l o y d Floyd Floyd始终关心的是三个点之间的关系,就算是在点对 ( s , t ) (s,t) (s,t)中仅仅只记录了 j j j这个点,但是在点对 ( s , j ) (s,j) (s,j)中也会记录i这个点,所以并没有遗漏点数的情况。
code1
#include<bits/stdc++.h>
using namespace std;
const int nn=203;
typedef long long ll;
int n,m;
int a[nn][nn];
ll cnt[nn][nn];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
int main()
{
n=read(), m=read();
memset(a,0x3f3f3f,sizeof(a));
memset(cnt,0x3f3f3f,sizeof(cnt));
for(int i=1;i<=m;++i)
{
int x=read(), y=read(), z=read();
a[x][y]=a[y][x]=min(a[x][y],z);
cnt[x][y]=cnt[y][x]=1;
}
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)/*注意这里应该是双向的*/
{
if(a[i][j]==a[i][k]+a[k][j]) cnt[i][j]=cnt[j][i]=cnt[i][j]+cnt[i][k]*cnt[k][j];
if(a[i][j]>a[i][k]+a[k][j])
{
a[i][j]=a[j][i]=a[i][k]+a[k][j];
cnt[i][j]=cnt[j][i]=cnt[i][k]*cnt[k][j];
}
}
bool im=false;
for(int k=1;k<=n;++k)/*判断每一个点*/
{
bool flag=false;
for(int i=1;i<=n;++i)
{
for(int j=i+1;j<=n;++j)
if(a[i][j]==a[i][k]+a[k][j]&&cnt[i][j]==cnt[i][k]*cnt[k][j])
{
flag=true;
break;
}
if(flag) break;
}
if(flag) im=true, printf("%d ",k);
}
if(!im) cout<<"No important cities."<<endl;
return 0;
}
code2
#include<bits/stdc++.h>
using namespace std;
const int nn=203;
int n,m;
int a[nn][nn],cnt[nn][nn];
bool v[nn];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
int main()
{
n=read(), m=read();
memset(a,0x3f3f3f,sizeof(a));
memset(cnt,-1,sizeof(cnt));
for(int i=1;i<=m;++i)
{
int x=read(), y=read(), z=read();
a[x][y]=a[y][x]=min(a[x][y],z);
}
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)/*要找到的是两个点之间必须要经历的点*/
{
if(a[i][j]==a[i][k]+a[k][j]) cnt[i][j]=cnt[j][i]=-1;/*当前这一点对可以通过其他的方式到达*/
if(a[i][j]>a[i][k]+a[k][j])
{
a[i][j]=a[j][i]=a[i][k]+a[k][j];
cnt[i][j]=cnt[j][i]=k;
}
}
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
if(cnt[i][j]!=-1)
v[cnt[i][j]]=true;
bool im=false;
for(int i=1;i<=n;++i)
if(v[i])
im=true, printf("%d ",i);
if(!im) cout<<"No important cities."<<endl;
return 0;
}
最后的小总结
- 做Floyd的题的时候需要仔细考虑Floyd的性质是什么?
- 做Floyd的题的时候需要注意一下数组的双向性,不能只更新数组矩阵的一半,另一半为空;
- 做Floyd的题的时候一般数据范围都比较明显;
- 细心,细心,细心!!
不乱于心,不困于情,不畏将来,不念过往,如此,安好。
完……