title: ACOJ 12330 最短路
categories: DFS
tags: [代码,编程]
date: 2019-03-17 19:55:00
Codeaha
<iframe height=500px src="https://www.acoj.com/problems/12330 " width="100%"></iframe>
给出一张N个点的无向图,问从1到N最多有可以画出多少条互不相交的路径,使得这些路径都是1到N的最短路。
在本题中,路径不相交的定义是:两条路径没有使用同一条边。
输入格式:
第一行包括两个整数n,m,代表点的个数和边的个数。
接下来m行,每行包括三个整数a,b,v,代表从a到b有一条距离为v的无向边。
输出格式:
输出一个数字,代表最多的互不相交的最短路的条数。
限制:
对于40%的数据,n,m<=10。
对于80%的数据,n,m<=100。
对于100%的数据,n,m<=1000,0<=v<=100
样例 1 :
输入:
3 3
1 2 1
1 3 1
2 3 1
输出:
1
看到这一题,首先想到的是SPFA
扫出最短路长度,然后就放弃去做下一题了
当我做完其他题目的一半时,想到了使用网络流
可能是对算法的不理解吧,这一题用网络流
根本无法解得,因为这不是流量问题,遇到需要使用同一条边时,不可能像网络流
那样退还之前路径的最小值,简而言之:路是有连续的,两条相反路径不能抵消,流量是没有这个性质的,相反方向的流量是可以抵消的,于是该方案领盒饭。
想到这个方法很自然吗!DFS
扫出1到N长度为SPFA
算出的最短长度。如果以后有边需要使用某一条边的话,使用增广路
的思想,让之前使用这一条边的路径重新找媳妇。
上面一种写到一半,忽然意识到不对,我为什么需要增广路呢?假设第k1
次DFS顺利完成,使用了N1
顶点,那么从N1
到重点的路径必定有至少一条是最短路里包含的!!!如果我需要在k2
次DFS时使用这一条路径,且没有N1
发出的另一条等长度路径,那么增广
必定失败,如果有,那么k2
次DFS可以直接使用这一路径证毕。
所以,我们只需要不停DFS直到没有新路径产生。
#include < cstdio>
#include < bits/stdc++.h>
#include < ctime>
using namespace std ;
int n,m,ans;
int T;
int head[2002 ],ver[2002 ],edge[2002 ],Next[2002 ],d[2002 ];
bool use[2002 ];
bool v[2002 ];
int tot;
int mem;
vector <int > line[1001 ];
int linetail;
queue<int > q;
int a,b,vv,len;
void SPFA ()// SPFA算法,详见:https://byhablog-1252071452.cos-website.ap-shanghai.myqcloud.com/2019/02/26/algorithmtips000/#SPFA-%E5%8D%95%E6%BA%90
{
memset (d,0x3f ,sizeof (d));
memset (v,0 ,sizeof (v));
d[1 ] = 0 ;
v[1 ] = 1 ;
q.push (1 );
while (q.size ())
{
int x = q.front ();
q.pop ();
v[x] = 0 ;
for (int i = head[x]; i != 0 ; i=Next[i])
{
int y = ver[i];
int z = edge[i];
if (d[y]>d[x]+z)
{
d[y] = d[x] + z;
if (!v[y])
{
q.push (y);
v[y] = 1 ;
}
}
}
}
}
void add (int x,int y,int z)// 邻接表,详见:https://byhablog-1252071452.cos-website.ap-shanghai.myqcloud.com/2019/02/26/algorithmtips000/#SPFA-%E5%8D%95%E6%BA%90
{
ver[++tot] = y;
edge[tot] = z;
Next[tot] = head[x];
head[x] = tot;
}
void DFS (int x,int step){
if (x == n){// 如果到了n顶点
if (step>len){// 路径超长
mem = 2 ;// 状态更改为2
return ;// 返回
}
ans++;// 路径为最短路
mem = 1 ;// 状态更改为1,为了把使用的边打标
return ;
}
if (step>=len){// 没到n顶点而路径超长一样炸回家,单程机票
return ;
}
for (int i = head[x];i!=0 ;i=Next[i]){// 邻接表扫边
if (!use[i]){// i号顶点是否被使用,false为没有被使用,true为被使用
use[i] = true ;// 先将路径标记为使用
DFS (ver[i],step+edge[i]);// 进行下一步的扫边
if (mem == 1 )// 扫到了最短路,不将标记点复原为false
return ;
use[i] = false ;// 路径超长或者路径超长^_^
}
}
return ;
}
int main ()
{
cin>>n>>m;
for (int i=1 ; i<=m; i++)// 加边入邻接表,注意是无向图,使用scanf而非cin可明显增加速度
{
scanf (" %d%d%d" ,&a,&b,&vv);
add (a,b,vv);
add (b,a,vv);
}
T = clock ();// 这里是超时保险丝,超时自动炸机
SPFA ();// SPFA扫最短路
len = d[n];// 将最短路存入len中
while (true ){
if (T<clock ()-920 )// 超时炸机
break ;
mem = 0 ;// 状态变量
DFS (1 ,0 );
if (mem == 0 ||mem == 2 )// 0为没有扫到路径,2为路径过长,在这里相当于没有扫到路径,其实可以将上面mem = 2 改为mem = 1或者直接删去,不影响,只是我懒得删除。
break ;
}
cout<<ans;
return 0 ;
}