并查集思想+dfs

HDU1716

排列2

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 5584    Accepted Submission(s): 2140


Problem Description

Ray又对数字的列产生了兴趣:

现有四张卡片,用这四张卡片能排列出很多不同的4位数,要求按从小到大的顺序输出这些4位数。

Input

每组数据占一行,代表四张卡片上的数字(0<=数字<=9),如果四张卡片都是0,则输入结束。 

Output

对每组卡片按从小到大的顺序输出所有能由这四张卡片组成的4位数,千位数字相同的在同一行,同一行中每个四位数间用空格分隔。

每组输出数据间空一行,最后一组数据后面没有空行。

Sample Input

1 2 3 4

1 1 2 3

0 1 2 3

0 0 0 0

Sample Output

1234 1243 1324 1342 1423 1432

2134 2143 2314 2341 2413 2431

3124 3142 3214 3241 3412 3421

4123 4132 4213 4231 4312 4321

 

1123 1132 1213 1231 1312 1321

2113 2131 2311

3112 3121 3211

 

1023 1032 1203 1230 1302 1320

2013 2031 2103 2130 2301 2310

3012 3021 3102 3120 3201 3210

 

 

这道题的题意很简单,思路也不难,但写起来有点费劲(直接调用库里面的有关输出字典序的函数(例如next_permutation)不在此讨论范围之列)。

若用dfs做,题目有3道坎要过:

1、当输入数字有重复时,如何保证输出不重复,例如输入 1 1 2 3,1出现了两次,所以在输出中就要注意别在第一行输出1123后,在第二行又输出1123等等(为了防止出现这种情况,这里用到了“并查集”给相同集合标号的思想,相同数字的下标划归为一个集合,如上例中,第一个1的下标为0,第二个1的下标为1,故0,1同属一个集合,集合的编号取第一个1的下标“0”)。

2、保证每行输出最后的数字没有空格

3、首数字变了之后要换行

 

代码如下:

#include<iostream>

#include<algorithm>

#include<cstring>

using namespace std;

 

int num[4];

int f[4];//为解决第一道坎,给集合标号,表示i属于标号为f[i]的集合

bool v[4];

bool flag;

void dfs(string s,int deep)

{

    if(deep==4)

    {

        if(flag) cout<<" ";

        cout<<s;

        flag=true;

        return;

    }

    bool visit[4];

    memset(visit,0,sizeof(visit));

    for(int i=0;i<4;i++)

    {

        if(deep==0&&num[i]==0)continue;

        if(v[i]) continue;

        if(visit[f[i]]) continue;

        visit[f[i]]=true;

        v[i]=true;

        string ss=s+char(num[i]+'0');

        dfs(ss,deep+1);

        v[i]=false;//典型回溯写法

        if(deep==0)//注意它的位置是在dfs后

        {

            cout<<endl;

            flag=false;

        }

    } 

}


int main()

{

    bool fflag=0;

    do

    {

        cin>>num[0]>>num[1]>>num[2]>>num[3];

        if(num[0]==0&&num[1]==0&&num[2]==0&&num[3]==0)

            break;

        if(fflag) cout<<endl;

        sort(num,num+4);

        for(int i=0;i<4;i++)

        {

            f[i]=i;//初始化f[i];

        }

        memset(v,0,sizeof(v));

        for(int i=0;i<3;i++)

        {

            if(num[i]==num[i+1])

                f[i+1]=f[i];//相当于并查集中的合并操作

        }

        string k="";

        flag=false;

        dfs(k,0);

        fflag=1;

    }while(true);

    return 0;

}

 

在这个程序中需要关注的是v数组(防止在输入为1 2 3 4时输出1111),visit数组(与f[i]共同解决第1道坎),flag(与dfs函数中的deep变量共同解决第二道坎),deep(解决第三道坎),fflag则是解决题目中output中最后组的输出不需要再加空行的要求。

需要注意的是visit数组是dfs定义的变量,所以相同深度(deep)的visit数组表示同一个东西,不同深度的visit数组是不同的,这里不要混淆,而v数组是在函数定义的,在任何深度的表示的东西都一样。

可以尝试改变一下dfs中的判断条件的位置或者改变一下v数组和visit数组等等,观察输出,便于更好地理解dfs。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章