用DFS来排座位

DFS(Depth-First-Search)深度优先算法,是搜索算法的一种。是一种在开发爬虫早期使用较多的方法。它的思想是从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底,这种尽量往深处走的概念即是深度优先的概念。

深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。

DFS

下面以一个具体问题来看DFS的实现与实用。(问题来源[GCJ2016-1A]BFFs)

Problem
You are a teacher at the brand new Little Coders kindergarten. You haveNkids in your class, and each one has a different student ID number from 1 throughN. Every kid in your class has a single best friend forever (BFF), and you know who that BFF is for each kid. BFFs are not necessarily reciprocal -- that is, B being A's BFF does not imply that A is B's BFF.
Your lesson plan for tomorrow includes an activity in which the participants must sit in a circle. You want to make the activity as successful as possible by building the largest possible circle of kids such that each kid in the circle is sitting directly next to their BFF, either to the left or to the right. Any kids not in the circle will watch the activity without participating.
What is the greatest number of kids that can be in the circle?
Input
The first line of the input gives the number of test cases,T.Ttest cases follow. Each test case consists of two lines. The first line of a test case contains a single integerN, the total number of kids in the class. The second line of a test case containsNintegersF1,F2, ...,FN, whereFiis the student ID number of the BFF of the kid with student ID i.
Output
For each test case, output one line containing "Case #x: y", where x is the test case number (starting from 1) and y is the maximum number of kids in the group that can be arranged in a circle such that each kid in the circle is sitting next to his or her BFF.
Limits
1 ≤T≤ 100.
1 ≤FiN, for all i.
Fi≠ i, for all i. (No kid is their own BFF.)
Small dataset
3 ≤N≤ 10.
Large dataset
3 ≤N≤ 1000.
Sample

Input
4
4
2 3 4 1
4
3 3 4 1
4
3 3 4 3
10
7 8 10 10 9 2 9 6 3 3
Output
Case #1: 4
Case #2: 3
Case #3: 3
Case #4: 6

In sample case #4, the largest possible circle seats the following kids in the following order:7 9 3 10 4 1. (Any reflection or rotation of this circle would also work.) Note that the kid with student ID 1 is next to the kid with student ID 7, as required, because the list represents a circle.

解释:
一个幼儿园,有N个小朋友,编号从1到N,每个孩子有一个永远最好的朋友。这个BFF不是相互的,A是B的BFF,不代表B也是A的的BFF。接下来明天有节课需要小朋友坐成一个圈玩游戏,但是每个孩子都希望自己的左边或者右边坐的是自己的BFF,没坐进去的小朋友就只能观看他们玩,那么这个圈最大能坐多少个小朋友?
输入第一个数T是测试数据的组数,然后每两行为一组,第一行一个数字是N小朋友的个数,第二行N个数字是编号1到N的孩子的最好朋友编号。
输出每行代表每组测试数据的最大圈坐小朋友数量。

分析:
第一步,我们需要找到每个孩子通过BFF这个关系单向能一共能链到多少孩子进来,这里就要用到DFS技术来实现。来看一下代码:

void dfs(int *input, bool *flag ,int start, NSMutableArray *array)
{
    if (flag[start - 1] == true) {
        return;
    }
    else
    {
        flag[start - 1] = true;
        [array addObject:[NSNumber numberWithInt:start]];
        start = input[start - 1];
        dfs(input, flag, start, array);
    }
}

这里input是BFF关系数组,flag是标志某一个小孩是否访问过的数组,start代表从哪个小孩开始搜索,array是最后我们要的链。从start传入的小孩编号开始搜索,将start自己写到array中并标记为已访问,然后start更新为自己的BFF,迭代调用dfs,直到走到标记为已访问节点停止搜索。然后将分别从1到N编号的小孩为起点的形成的最长链的都存储起来。这里我们存在chainArray里面,方便后面使用。

第二步,我们用刚才保存的链来拼接环,这里我们用一个数组来记录以每个孩子为起点的最大环长度circle,同时用一个整数maxNum记录所有的环中最大值。然后我们开始遍历刚才记录在chainArray里面的链,计算他们能拼接最长环的大小。计算中分三种情况:
1、链的最后一位小朋友的最好朋友不是第一位与倒数第二位小孩。如:1->2->3->4,但是4的BFF是2,那么这种链就是无效的,也不能够成环,长度就记为0;
2、链的最后一位小朋友的最好朋友是第一位小孩。如:1->2->3->4,4的BFF是1,那么这种链就是自成环,长度记为链长度4。
3、链的最后一位小朋友的最好朋友是倒数第二位小孩。如:1->2->3->4,4的BFF是3,这种链既自成环,又可以和其它以3结尾的链拼接成环,如5->4->3,两个链拼接后就成了1->2->3<->4<-5。对于这种情况,我们就必须再次遍历所有链,找出和该链拼接后的最长链,记录为两链长度和减2,并保存进circle中。
最后,每次更新过后环长度过后,需要更新maxNum的值。
实现代码如下:

    for (int j = 0; j < N; j++) {
        NSArray *array = [chainArray objectAtIndex:j];
        int length = (int)array.count;
        int LastIndex = [[array lastObject] intValue];
        int nextValue = input[LastIndex - 1];
        if (nextValue != [[array firstObject] intValue] && nextValue != [[array objectAtIndex:array.count - 2] intValue]) {
            length = 0;
        }
        else
        {
            if (nextValue == [[array objectAtIndex:array.count - 2] intValue]) {
                int MaxLength = 0;
                for (NSArray *item in chainArray) {
                    int newLength = 0;
                    if ([[item lastObject] intValue] == nextValue) {
                        newLength = length + (int)[item count] - 2;
                    }
                    MaxLength = MAX(MaxLength, newLength);
                }
                length = MaxLength;
                if (circle[nextValue - 1] < length) {
                    circle[nextValue - 1] = length;
                }
            }
        }
        
        maxNum = MAX(maxNum, length);
    }

至此,我们基本找到了以所有孩子为起点能形成的单一环,但是并不是能形成的最大环。

第三步,找出最大的环大小,上面的maxNum已经记录了单环所能得到的最大值,但是对于如1->2->3<->4<-5这种回环,其实还是可以和其它回环进行拼接,如和6->7<->8<-9拼接,最后得到更大的环1->2->3<->4<-5\6->7<->8<-9。所以我们需要把刚才circle中记录的回环长度都加起来除以2,再来更新maxNum得到最终的结果,代码如下:

    int tot = 0;
    for (int k = 0; k < N; k ++) {
        tot += circle[k];
    }
    maxNum = MAX(maxNum, tot / 2);
    return maxNum;

至此,我们就完美的利用DFS完成了对小朋友排座位的任务。
看一下输出判定结果:

Small input
16 pointsSolve C-small
Judge's response for last submission: Correct.
Large input
29 pointsSolve C-large
Judge's response for last submission: Correct.

完整代码:

//
//  main.m
//  BFFs
//
//  Created by Jiao Liu on 4/16/16.
//  Copyright © 2016 ChangHong. All rights reserved.
//

#import <Foundation/Foundation.h>

void dfs(int *input, bool *flag ,int start, NSMutableArray *array)
{
    if (flag[start - 1] == true) {
        return;
    }
    else
    {
        flag[start - 1] = true;
        [array addObject:[NSNumber numberWithInt:start]];
        start = input[start - 1];
        dfs(input, flag, start, array);
    }
}

long maxCircle(int *input, int N)
{
    long maxNum = 0;
    
    NSMutableArray *chainArray = [NSMutableArray array];
    int circle[N];
    memset(circle, 0, sizeof(circle));
    for (int i = 0; i < N; i++) {
        bool flag[N];
        memset(flag, false, sizeof(flag));
        NSMutableArray *array = [NSMutableArray array];
        dfs(input, flag, i + 1, array);
        [chainArray addObject:array];
    }
    
    for (int j = 0; j < N; j++) {
        NSArray *array = [chainArray objectAtIndex:j];
        int length = (int)array.count;
        int LastIndex = [[array lastObject] intValue];
        int nextValue = input[LastIndex - 1];
        if (nextValue != [[array firstObject] intValue] && nextValue != [[array objectAtIndex:array.count - 2] intValue]) {
            length = 0;
        }
        else
        {
            if (nextValue == [[array objectAtIndex:array.count - 2] intValue]) {
                int MaxLength = 0;
                for (NSArray *item in chainArray) {
                    int newLength = 0;
                    if ([[item lastObject] intValue] == nextValue) {
                        newLength = length + (int)[item count] - 2;
                    }
                    MaxLength = MAX(MaxLength, newLength);
                }
                length = MaxLength;
                if (circle[nextValue - 1] < length) {
                    circle[nextValue - 1] = length;
                }
            }
        }
        
        maxNum = MAX(maxNum, length);
    }
    
    int tot = 0;
    for (int k = 0; k < N; k ++) {
        tot += circle[k];
    }
    maxNum = MAX(maxNum, tot / 2);
    return maxNum;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        freopen("/Users/Jiao/Desktop/CodeJam/BFFs/C-large-practice.in", "r", stdin);
//        freopen("/Users/Jiao/Desktop/CodeJam/BFFs/C-large-practice.out", "w", stdout);
        int T;
        scanf("%d",&T);
        for (int i = 1; i<=T; i++) {
            int N;
            scanf("%d",&N);
            int Bffs[N];
            for (int j = 0; j<N; j++) {
                int bestFriend;
                scanf("%d",&bestFriend);
                Bffs[j]=bestFriend;
            }
            printf("Case #%d: %ld\n",i,maxCircle(Bffs,N));
        }
    }
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容