1. 程式人生 > >HDU-1116-Play on Words (並查集 +歐拉回路)

HDU-1116-Play on Words (並查集 +歐拉回路)

原題連結
http://acm.hdu.edu.cn/showproblem.php?pid=1116
Some of the secret doors contain a very interesting word puzzle. The team of archaeologists has to solve it to open that doors. Because there is no other way to open the doors, the puzzle is very important for us.

There is a large number of magnetic plates on every door. Every plate has one word written on it. The plates must be arranged into a sequence in such a way that every word begins with the same letter as the previous word ends. For example, the word acm'' can be followed by the word

motorola’’. Your task is to write a computer program that will read the list of words and determine whether it is possible to arrange all of the plates in a sequence (according to the given rule) and consequently to open the door.
Input
The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing a single integer number Nthat indicates the number of plates (1 <= N <= 100000). Then exactly Nlines follow, each containing a single word. Each word contains at least two and at most 1000 lowercase characters, that means only letters ‘a’ through ‘z’ will appear in the word. The same word may appear several times in the list.
Output
Your program has to determine whether it is possible to arrange all the plates in a sequence such that the first letter of each word is equal to the last letter of the previous word. All the plates from the list must be used, each exactly once. The words mentioned several times must be used that number of times.
If there exists such an ordering of plates, your program should print the sentence “Ordering is possible.”. Otherwise, output the sentence “The door cannot be opened.”.
Sample Input
3
2
acm
ibm
3
acm
malform
mouse
2
ok
ok
Sample Output
The door cannot be opened.
Ordering is possible.
The door cannot be opened.
題意:
有n個字串,想讓這n個字串首尾相連形成一個長字串,要求連線時,下一個字串的首個字元一定與前一個字串的最後一個字串相同。問能否形成這樣的長字串。
題解:
類似於形成歐拉回路,要求每一種出現過的字元的入度等於出度,與此同時,也可以存在一個入度比出度大一和一個出度比入度大一的字串作為長字串的首尾,其餘的插在中間。但僅僅這樣是不行的,還需要能夠連線起來,即只有棵樹,如果有多棵樹,也不能夠連起來。(這裡就用到了並查集)
附上AC程式碼:

#include <iostream>
#include <cstring>
#include <map>
#include <cmath>
using namespace std;
int f[26],vis[26];
int findf(int i)//尋找根節點
{
    return f[i]==i?i:findf(f[i]);
}
void setf(int i,int j)//設定關係
{
    int a=findf(i),b=findf(j);
    if(a!=b)
        f[a]=b;
}
int main()
{
    ios::sync_with_stdio(false);
    int t,n;
    char s[1005];
    cin>>t;
    while(t--)
    {
        int cnt=0;//記錄有幾棵樹,如果超過一棵,則一定不能聯通
        int cnst=0;//入讀比出度大一的個數
        int cned=0;//出讀比入度大一的個數
        int flag=0;//標記能否滿足題意
        char tmp='a';
        map<char,int> st;//定義字元與數量的關係,用來記錄入度和出度
        map<char,int> ed;
        memset(vis,0,sizeof(vis));//初始化
        for(int i=0;i<26;i++)//初始化
            f[i]=i;
        for(int i=1;i<=26;i++)//初始化
        {
            st[tmp]=0;
            ed[tmp]=0;
            tmp++;
        }
        cin>>n;
        for(int i=0;i<n;++i)
        {
            cin>>s;
            int len=strlen(s);
            int start=s[0]-'a';
            int ends=s[len-1]-'a';
            st[s[0]]++;
            ed[s[len-1]]++;
            vis[start]=1;//出現過
            vis[ends]=1;
            setf(start,ends);//連線
        }
        for(int i=0;i<26;i++)//查詢有幾顆子樹
            if(vis[i] && f[i]==i)
                cnt++;
        if(cnt>1)//超過一棵樹,則一定無法滿足題意
        {
           cout<<"The door cannot be opened."<<endl;
           continue;
        }
        tmp='a';
        for(int i=1;i<=26;++i)//判斷能否練成一條線
        {
            int d=abs(st[tmp]-ed[tmp]);//記錄每一個字元的入度與出度的差的絕對值
            if(d==0)//剛好相同,不用管
            {
                tmp++;
                continue;
            }
            else
            {
                if(d==1)//差值為1
                {
                    if(st[tmp]-ed[tmp]==1)//入度比出度大
                        cnst++;
                    else//入度比出度小
                        cned++;
                }
                if(cnst>1||cned>1)//入度出度不等的點超過1個,一定不能滿足題意
                {
                    flag=1;
                    break;
                }
                if(d>=2)//入度和出度差值大於2,一定不能連成線
                {
                    flag=1;
                    break;
                }
            }
            tmp++;
        }
        if(cnst+cned==1)//如果只有一個入度比出度大的則不行
            flag=1;
        if(flag)
            cout<<"The door cannot be opened."<<endl;
        else
            cout<<"Ordering is possible."<<endl;
    }
    return 0;
}

歡迎評論!