dfs问题总结

目录:

 1. 解题方法及何时需要回溯
 2. 树中的dfs
 3. 图中的dfs

一、解题方法:
就我目前做的一些简单及中等的dfs来看,大部分的dfs都可以用一个模版来表示:

void dfs(起点,变化的量){
    if(达到递归的边界){
        做一系列操作;
        return;
    }
    (如果需要回溯,这里进入)
    dfs(相邻点,更新的变化的量);
    (如果需要回溯,这里出去)

}

注意事项:这里引用树中dfs的第二题来进行说明,先贴上完整代码。

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<map>
using namespace std;
const int maxn = 1110;
int sum;
struct node{
    node* right;
    node* left;
    int data;
}tree[maxn];
vector<node*> A;

int n;

int getheight(node* root){
    if(root==NULL) return 0;
    else if(root->left==NULL&&root->right==NULL){
        return 1;
    }
    else return max(getheight(root->left),getheight(root->right))+1;

}
void insert(node*& root,int data){
    if(root==NULL){
        root = new node;
        root->data = data;
        root->right = NULL;
        root->left = NULL;
    }
    else if(root->data>data){
        insert(root->left,data);
    }else{
        insert(root->right,data);
    }
}
node* create(node* root,int temp[]){
    for(int i = 0;i<n;++i){
        insert(root,temp[i]);
    }
    return root;
}
vector<int> ans;


void dfs(node* root,int facsum,vector<int> temph){
    if(root==NULL) return;
    temph.push_back(root->data);
    facsum+=root->data;
    if(facsum>sum) return;
    if(facsum==sum){
        if(root->left!=NULL||root->right!=NULL) return;
        else{
            ans = temph;
            for(int i = 0;i<ans.size();++i){
                cout<<ans[i];
            }
            cout<<endl;
            return;
        }      
    }
    if(root->left!=NULL){    
        dfs(root->left,facsum,temph);          
    }
    if(root->right!=NULL){
        dfs(root->right,facsum,temph);
    }   
}

int main(){
    int result[110];
    vector<int> temph;
    node* root  = NULL;
    scanf("%d%d",&n,&sum);
    for(int i = 0;i<n;++i){
        scanf("%d",&result[i]);
    }
    root = create(root,result);
    dfs(root,0,temph);
    cout<<ans.size()<<endl;
    return 0; 
}

input:
8 27
8 7 6 4 2 10 9 12
这里主要注意dfs那个函数,首先看它调用的参数,node* root,int facsum,vector temph。这几个都是形参,在随着递归和return的过程中不断的进行变化的。因此按这种方式是不需要写回溯的。
此时的正确输出为:
87642
8109
3
那什么情况需要回溯呢。当我们将vector temph改成vector &temph加了一个引用,这时我认为可以把temph当成一个全局变量,此时的错误输出为:
87642
87642109
7
那么该如何使其正确呢,我们应该加入回溯的部分让temp进行返回(成为全局变量之后不能跟着函数返回而返回),返回的地点也就是函数结束的时候。
下面贴上更改后的代码:

void dfs(node* root,int facsum,vector<int> &temph){
    if(root==NULL) return;
    temph.push_back(root->data);
    facsum+=root->data;
    if(facsum>sum) return;
    if(facsum==sum){
        if(root->left!=NULL||root->right!=NULL) return;
        else{
            ans = temph;
            for(int i = 0;i<ans.size();++i){
                cout<<ans[i];
            }
            cout<<endl;
            return;
        }      
    }
    if(root->left!=NULL){   
        dfs(root->left,facsum,temph);
        temph.pop_back();          
    }
    if(root->right!=NULL){
        dfs(root->right,facsum,temph);
        temph.pop_back();
    }   
}

由这个示例,可知全局变量和局部变量的区别之大。我们在设置形参的过程中要搞清楚它充当的是一个怎样的角色,再确定它是局部变量还是全局变量。

二、树中的dfs:
第一题:PTA1079

A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone involved in moving a product from supplier to customer.
Starting from one root supplier, everyone on the chain buys products from one’s supplier in a price P and sell or distribute them in a price that is r% higher than P. Only the retailers will face the customers. It is assumed that each member in the supply chain has exactly one supplier except the root supplier, and there is no supply cycle.
Now given a supply chain, you are supposed to tell the total sales from all the retailers.
思路:对树从根部进行dfs,递归出口设置为叶子节点,在递归中持续更新变量depth,每个递归出口进行计算,这里要注意,根节点的深度是0.

#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int maxn = 100010;
struct node{
    double data;
    vector<int> child;
}Node[maxn];

int n;
double p,r,ans=0;
void dfs(int index,int depth)
{
    if(Node[index].child.size()==0)
    {
        ans += Node[index].data*pow(1+r,depth);
        return;
    }
    for(int i = 0;i<Node[index].child.size();++i)
    {
        dfs(Node[index].child[i],depth+1);
    }

}
int main()
{
    int k,child;
    scanf("%d%lf%lf",&n,&p,&r);
    r /=100;
    for(int i = 0;i<n;++i)
    {
        scanf("%d",&k);
        if(k==0)
        {
            scanf("%lf",&Node[i].data);

        }else{
            for(int j = 0;j<k;++j)
            {
                scanf("%d",&child);
                Node[i].child.push_back(child);

            }

        }
    }
    dfs(0,0);
    printf("%.1f\n",p*ans);
    return 0;
}

第二题:
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
剑指offer
思路:这道题目做了挺久一直没调试出来,后来才知道当我把数组定义在形参之中进行递归时,是不需要回溯的,只有那种全局的变量类似你定义的visit[]数组之类的才需要回溯,还有题目中要求体现的数组长度大的数组靠前,我在代码中未体现,若要实现就是将多条路径储存在二维数组中最后进行比较就可以了。

void dfs(node* root,int facsum,vector<int> temph){
    if(root==NULL) return;
    temph.push_back(root->data);
    facsum+=root->data;
    if(facsum>sum) return;
    if(facsum==sum){
        if(root->left!=NULL||root->right!=NULL) return;
        else{
            ans = temph;
            for(int i = 0;i<ans.size();++i){
                cout<<ans[i];
            }
            cout<<endl;
            return;
        }      
    }
    if(root->left!=NULL){    
        dfs(root->left,facsum,temph);          
    }
    if(root->right!=NULL){
        dfs(root->right,facsum,temph);
    }   
}

三、图中的dfs

第一题:
PTA1013(battle over cities)
It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.
For example, if we have 3 cities and 2 highways connecting city​1​​-city​2​​ and city​1​​-city​3​​. Then if city​1​​ is occupied by the enemy, we must have 1 highway repaired, that is the highway city​2​​-city​3​​.

思路:题目的意思是求出删除了一个顶点之后的图使其连通需要的边数,这就需要我们对所有未被访问且不是删除点的顶点进行dfs,同时设置一个block记录进入dfs的次数,block-1就是需要增加的条数。

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N = 1111;
vector<int> G[N];
bool vis[N];
int currentpoint;
void dfs(int v)
{
   
    vis[v] = true;
    for(int i = 0;i<G[v].size();++i)
    {
        if(vis[G[v][i]]==false&&G[v][i]!=currentpoint)
        {
            dfs(G[v][i]);
        }
    }
}

int n,m,k;
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 0;i<m;++i)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        G[a].push_back(b);
        G[b].push_back(a);
    }
    for(int query = 0;query<k;++query)
    {
        scanf("%d",&currentpoint);
        memset(vis,false,sizeof(vis));
        int block = 0;
        for(int i = 1;i<=n;++i)
        {
            if(i!=currentpoint&&vis[i]==false)
            {
                dfs(i);
                block++;
            }
        }
        printf("%d\n",block-1);
    }
    return 0;
}

第二题:
PTA 1034 Head of a Gang
One way that the police finds the head of a gang is to check people’s phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone calls made between the two persons. A “Gang” is a cluster of more than 2 persons who are related to each other with total relation weight being greater than a given threshold K. In each gang, the one with maximum total weight is the head. Now given a list of phone calls, you are supposed to find the gangs and the heads.
思路
首先是对名字的处理办法,直接对string类型难以处理,因此我们使用两个mp数组将其与int类型互相映射。这里也将dfs中head等参数加上&使其成为全局变量,并在dfs过程中不停地更新head

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<map>
using namespace std;
map<string,int> stringtoint;
map<int,string> inttostring;
map<string,int> gang;
int numperson = 0;
int n,k;
int weight[2010] = {0};
int G[2010][2010] = {0};
bool visit[2010] = {false};
void dfs(int nowvisit,int &head,int &nummember,int &totalvalue){
	nummember++;
	visit[nowvisit] = true;
	if(weight[nowvisit]>weight[head]){
		head = nowvisit;
	}
	for(int i=0;i<numperson;++i){
		if(G[nowvisit][i]>0)
		{
			totalvalue+=G[nowvisit][i];
			G[nowvisit][i] = G[i][nowvisit] = 0;
			if(visit[i]==false){
				dfs(i,head,nummember,totalvalue);
			}
		}
	}

}
void dfstravel(){
	for(int i = 0;i<numperson;++i){
		if(visit[i]==false){
			int head = i,nummember = 0,totalvalue = 0;
			dfs(i,head,nummember,totalvalue);
			if(nummember>2&&totalvalue>k){
				gang[inttostring[head]] = nummember;
			}
		}
	}
}



int change(string str)
{
	if(stringtoint.find(str)!=stringtoint.end()){
		return stringtoint[str];
	}else{
		stringtoint[str] = numperson;
		inttostring[numperson] = str;
		return numperson++;
	}
}
int main(){
	string str1,str2;
	int w;
	cin>>n>>k;
	for(int i = 0;i<n;++i){
		cin>>str1>>str2>>w;
		int id1 = change(str1);
		int id2 = change(str2);
		weight[id1]+=w;
		weight[id2]+=w;
		G[id1][id2]+=w;
		G[id2][id1]+=w;
	}
	dfstravel();
	cout<<gang.size()<<endl;
	map<string,int>::iterator it;
	for(it = gang.begin();it!=gang.end();it++){
		cout<<it->first<<" "<<it->second<<endl;
	}
	return 0;

}
发布了2 篇原创文章 · 获赞 0 · 访问量 132

猜你喜欢

转载自blog.csdn.net/weixin_44879587/article/details/105034862