问题描述
X 国的一个网络使用若干条线路连接若干个节点。节点间的通信是双向的。某重要数据包,为了安全起见,必须恰好被转发两次到达目的地。该包可能在任意一个节点产生,我们需要知道该网络中一共有多少种不同的转发路径。
源地址和目标地址可以相同,但中间节点必须不同。
如下图所示的网络。
1 -> 2 -> 3 -> 1 是允许的
1 -> 2 -> 1 -> 2 或者 1 -> 2 -> 3 -> 2 都是非法的。
输入格式
输入数据的第一行为两个整数N M,分别表示节点个数和连接线路的条数(1<=N<=10000; 0<=M<=100000)。
接下去有M行,每行为两个整数 u 和 v,表示节点u 和 v 联通(1<=u,v<=N , u!=v)。
输入数据保证任意两点最多只有一条边连接,并且没有自己连自己的边,即不存在重边和自环。
输出格式
输出一个整数,表示满足要求的路径条数。
样例输入1
3 3
1 2
2 3
1 3
样例输出1
6
样例输入2
4 4
1 2
2 3
3 1
1 4
样例输出2
10
这是一道看起来十分简单的题目。
只要进行深搜就可以了,可是却让我发现了我之前一直没发现的自己编程上面常常犯的错误。就是想当然,不分实际情况,例如
在深搜回溯的时候 我一般想当然 在DFS之前vis=1在这之后vis=0;然而这道题目却告诉我不能随便改变DFS的初始状态
比如说下面的代码 我怎么也想不到会错
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
using namespace std;
const int maxn=10100;
int n,m;
int u,v;
vector<int>G[maxn];
int vis[maxn]={0};
int ans=0;
vector<int>path;
void dfs(int s,int x,int in){
if(in==3){ans++;
for(int i=0;i<path.size();i++)
cout<<path[i]<<" ";
cout<<endl;
return;}
else{
for(int i=0;i<G[x].size();i++)
{int v=G[x][i];
if(vis[v]==0||(in==2&&v==s))
{
vis[v]=1;//错就错在这里啊
path.push_back(v);
dfs(s,v,in+1);
path.pop_back();
vis[v]=0;//这里如果第四个结点放的是和第一个结点一样数,vis就把这个结点的状态变了 导致下一轮的时候错了,可以把v 改成x
}
}
} }
int main()
{
freopen("d://jin.txt","r",stdin);
cin>>n>>m;
while(m--){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;i++){
vis[i]=1;
path.push_back(i);
dfs(i,i,0);
path.pop_back();
vis[i]=0;
}
cout<<ans;
return 0;
}
再比如说下面的代码 导致的就是比答案少了
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
using namespace std;
const int maxn=10100;
int n,m;
int u,v;
vector<int>G[maxn];
int vis[maxn]={0};
int ans=0;
vector<int>path;
void dfs(int s,int x,int in){
vis[x]=1;//原本以为在dfs外面改变vis和里面是一样的,没想到啊
if(in==3){ans++;
for(int i=0;i<path.size();i++)
cout<<path[i]<<" ";
cout<<endl;
return;}//错在这里 提前return了状态都没变回来啊
else{
for(int i=0;i<G[x].size();i++)
{int v=G[x][i];
if(vis[v]==0||(in==2&&v==s))
{
path.push_back(v);
dfs(s,v,in+1);
path.pop_back();
}
}
} vis[x]=0;}
int main()
{
freopen("d://jin.txt","r",stdin);
cin>>n>>m;
while(m--){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;i++){
path.push_back(i);
dfs(i,i,0);
path.pop_back();//还是应该在这种地方先搞vis 因为这也是在一个循环里面的
}
cout<<ans;
return 0;
}
下面是一种正确的方法
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
using namespace std;
const int maxn=10100;
int n,m;
int u,v;
vector<int>G[maxn];
int vis[maxn]={0};
int ans=0;
vector<int>path;
void dfs(int s,int x,int in){
if(in==3){ans++;
for(int i=0;i<path.size();i++)
cout<<path[i]<<" ";
cout<<endl;
return;}
else{
for(int i=0;i<G[x].size();i++)
{int v=G[x][i];
if(vis[v]==0||(in==2&&v==s))
{
if(v!=s)vis[v]=1;
path.push_back(v);
dfs(s,v,in+1);
path.pop_back();
if(v!=s)vis[v]=0;
}
}
} }
int main()
{
freopen("d://jin.txt","r",stdin);
cin>>n>>m;
while(m--){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;i++){
vis[i]=1;
path.push_back(i);
dfs(i,i,0);
path.pop_back();
vis[i]=0;
}
cout<<ans;
return 0;
}
所谓回溯,就是回到初始的状态