DFS遍历图时的小技巧

DFS遍历图时的小技巧

我们通常使用DFS遍历图时,用vis[i]=true;来标记访问过的节点,但是如果要让我们统计图中所有边的长度的话,如果我们还这样做的话,对于非环形图来说,没问题,但是对于环形图来说,就可能访问不到最后一条边,如A-B-C-A,A标记之后就不能统计到C-A了。
这时我们的办法是,每访问一条边后,就把它销毁,然后递归地去DFS时不再以vis[i]==false为条件去递归,而是直接以G[i][j]!=0为条件去递归。
这也是和平时DFS遍历图时有区别的地方,一个小技巧吧。

后来又看到别人的解法,先访问边,再判断vis[i],再去DFS,可见下面的DFS法二。

应用:1034 Head of a Gang (30 分)

//此题的难点在于DFS遍历每一个点后,都销毁走过的路径,不走回头路。
//在递归时,不再以vis[i]==false问判断条件 ,因为vis[i]=true标记后,如果是环形图的话,那么最后一条边就不能访问到
//如A-B-C-A,刚开始时A被标记后,那么C-A这条边就不能在被访问到。
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;

const int maxn=2010;//不能定义为1010时,会有段错误,必须定义为2000以上,由于通话记录有1000条,因此不同的人可能有2000个
struct Gang{
    int head;
    int num;
};
int G[maxn][maxn];
int W[maxn];
bool vis[maxn];
int n,K;
vector<Gang> ans;
map<string,int> stringToInt;
map<int,string> intToString;
int numPerson;
int maxConnTime;
void DFS(int idx,int &total,int &num,int &head){
    if(vis[idx]==false){
        num++;
        vis[idx]=true;
    }
    if(W[idx]>maxConnTime){
        maxConnTime=W[idx];
        head=idx;
    }
    for(int i=0;i<numPerson;i++){
        if(G[idx][i]){//这里不在以vis[i]==false为条件,因为对于环形图访问不到最后一条边
            total+=G[idx][i];
//          num++;
            G[idx][i]=G[i][idx]=0;//访问后销毁边
            DFS(i,total,num,head);
        }
    }
}
//法二 :先访问边,再DFS遍历
/**
void DFS(int idx,int &total,int &num,int &head){
    num++;
    vis[idx]=true;
    if(W[idx]>maxConnTime){
        maxConnTime=W[idx];
        head=idx;
    }
    for(int i=0;i<numPerson;i++){
        if(G[idx][i]){
            total+=G[idx][i];
            G[idx][i]=G[i][idx]=0;
            if(vis[i]==false){
                DFS(i,total,num,head);
            }
        }
    }
}
*/
bool cmp(Gang a,Gang b){
    return intToString[a.head]<intToString[b.head];
}
int transfer(string name){
    if(stringToInt.find(name)==stringToInt.end()){
        stringToInt[name]=numPerson;
        intToString[numPerson]=name;
        return numPerson++;
    }else{
        return stringToInt[name];
    }
}
int main(){
    cin>>n>>K;
    for(int i=0;i<n;i++){
        string a,b;
        int w;
        cin>>a>>b>>w;
        int aId=transfer(a);
        int bId=transfer(b);
        G[aId][bId]+=w;
        G[bId][aId]=G[aId][bId];
        W[aId]+=w;
        W[bId]+=w;
    }
    for(int i=0;i<numPerson;i++){
        if(vis[i]==false){
            int total=0,num=0,head;
            maxConnTime=0;
            DFS(i,total,num,head);
            if(total>K&&num>2){
                Gang gang;
                gang.head=head,gang.num=num;
                ans.push_back(gang);
            }
        }
    }
    sort(ans.begin(),ans.end(),cmp);
    cout<<ans.size()<<endl;
    for(int i=0;i<ans.size();i++){
        cout<<intToString[ans[i].head]<<" "<<ans[i].num<<endl;
    }

    return 0;
}

原文地址:https://www.cnblogs.com/sqmax/p/10084360.html

时间: 2024-10-25 15:16:43

DFS遍历图时的小技巧的相关文章

邻接表存储图,DFS遍历图的java代码实现

import java.util.*; public class Main{ static int MAX_VERTEXNUM = 100; static int [] visited = new int[MAX_VERTEXNUM]; public static void main(String [] args){ Graph G = new Graph(); creatGraph(G); output(G); for(int i=0;i<G.vertex_num;i++) visited[i

uva 784 Maze Exploration(DFS遍历图)

uva 784 Maze Exploration A maze(迷宫) of rectangular(矩形的) rooms is represented on a two dimensional(空间的) grid as illustrated(阐明) in figure 1a. Each point of the grid is represented by a character. The points of room walls are marked by the same charact

前端切图的一些小技巧

昨天晚上花了1个小时把慕课网上的PS切图教程看了一遍,发现之前用的切图方式有点low,还学习到一些设置技巧,在此记录. 1.新建页面,背景内容设置为透明,自动选择不用打勾,后面的框选图层,这样按住ctrl鼠标点击就能快速找到对应图层 2.标尺和智能参考线都打上勾 3.历史记录,图层,信息,字符4个面板都打开,面板选项中鼠标坐标设置为像素,文档尺寸打勾,颜色都选为RGB 4.新建工作区,存储 5.按shift可以智能吸附 6.保存时的jpg品质60刚刚好,画质好体积又小 7.图片不是太大的时候,可

Windows下程序打包发布时的小技巧(使用Dependency Walker侦测不理想,改用VS自带的dumpbin则万无一失,还可查看dll导出的函数)

Windows下开发的应用程序在发布时,需要将其依赖的一些动态链接库一起打进安装包里面去.这个时候,快速确定这个程序到底依赖哪些动态链接库变得非常重要.很久以前写过一篇关于Qt程序安装包制作的博客,里面介绍了Dependency Walker这个小工具.但是实际操作起来并不理想,因为Dependency Walker将exe文件依赖的所有动态库以及动态库所依赖的动态库都列出来了.看得你一脸懵逼: 你很难分清楚:倒底哪些DLL是需要打包的,哪些DLL是系统自带的.而作为打包过程中的一个小步骤,我根

使用jquery时一些小技巧的总结

使用 each 遍历 var nodes = Ztree.getCheckedNodes(true); //获取所有勾选的节点 $.each(nodes,function(i,value){ alert(value.name); }) js对象的声明和二维数组的创建 var ids_str = {}; $.each(nodes,function(i,value){ ids_str[i] = {}; //这一步不可或缺,声明二维也是一个对象,才能进行下面的赋值 ids_str[i]['id'] =

ruby调试/练习时的小技巧

必备工具 irb 查祖先 1.9.3-p545 :023 > String.ancestors => [String, Comparable, Object, Kernel, BasicObject] String的前面有四个上级 过滤方法 Ruby的方法非常多,以至于不得不用grep了. 1.9.3-p545 :049 > [].methods.grep /^me/ => [:member?, :methods, :method] 查方法来源 1.9.3-p545 :018 &g

java.text.MessageFormat格式化字符串时的小技巧

public static void main(String[] args) throws InterruptedException { MessageFormat form = new MessageFormat( "{2,date,yyyy-MM-dd HH:mm:ss.SSS} The disk \"{1}\" contains {0,number,#.##} file(s).{3}"); int fileCount = 1273273237; String

ios中集合遍历方法的比较和技巧

本文原文发表自我的[自建博客],cnblogs同步发表,格式未经调整,内容以原博客为准 我是前言 集合的遍历操作是开发中最常见的操作之一,从C语言经典的for循环到利用多核cpu的优势进行遍历,开发中ios有若干集合遍历方法,本文通过研究和测试比较了各个操作方法的效率和优略势,并总结几个使用集合遍历时的小技巧. ios中常用的遍历运算方法 遍历的目的是获取集合中的某个对象或执行某个操作,所以能满足这个条件的方法都可以作为备选: 经典for循环 for in (NSFastEnumeration)

Cocos2dx 小技巧(十二) 一种可行的系列动画播放方式

定义: 将一个类(Adaptee)的接口转换成客户(Client)希望的另外一个接口(Target). 目标接口(Target):客户所期待的接口.目标可以是具体的或抽象的类,也可以是接口. 需要适配的类(Adaptee):需要适配的类或适配者类. 适配器(Adapter):使得一个东西适合另一个东西的东西.百度中定义为:接口转换器.通过包装一个需要适配的对象,把源接口转换成目标接口. 为什么要适配:需要的东西已做好,但是不能用,短时间又不能改造,想办法适配它. 作用: 使得原本由于接口不兼容而