Luogu P1278 单词游戏(dfs)

P1278 单词游戏

题意

题目描述

\(Io\)\(Ao\)在玩一个单词游戏。

他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致。

游戏可以从任何一个单词开始。

任何单词禁止说两遍,游戏中只能使用给定词典中含有的单词。

游戏的复杂度定义为游戏中所使用的单词长度总和。

编写程序,求出使用一本给定的词典来玩这个游戏所能达到的游戏最大可能复杂度。

输入输出格式

输入格式:

输入文件的第一行,表示一个自然数\(N(1 \leq N \leq 16)\)\(N\)表示一本字典中包含的单词数量以下的每一行包含字典中的一个单词,每一个单词是由字母\(A\)\(E\)\(I\)\(O\)\(U\)组成的一个字符串,每个单词的长度将小于等于\(100\),所有的单词是不一样的。

输出格式:

输出文件仅有一行,表示该游戏的最大可能复杂度。

输入输出样例

输入样例:

5
IOO
IUUO
AI
OIOOI
AOOI

输出样例:

16

思路

骗分过样例,暴力出奇迹! --sbw

骗分用爆搜过了一道记忆化搜索题,有点开心。

这题的爆搜其实十分好写,而\(n\)又很小,不过细细想来,如果每次\(dfs\)时全部跑满,时间复杂度是\(O(n!)\)的,那么对于最大的数据范围:

\[16!=20922789888000=TLE\]

显然就会爆炸。那么想想,出题人会怎么卡你呢?首先,他可以做一个环:

IOI
IOOI
IOOOI
IOOOOI
IOOOOOI
...

这样的话每种情况都能转移到,就会把爆搜的时间复杂度跑满。针对这样的出题人,我们只需要记录所有字符串的长度和(也就是最大可能答案),一旦搜到就赶紧退出,就不会\(TLE\)了。

狡猾的出题人也想到了这一点,于是造出了这样的数据:

UE
IOI
IOOI
IOOOI
IOOOOI
IOOOOOI
...

不过这也好办,我们在总长度中减去永远不会遍历到的字符串来更新就好了。

于是这题就被这么水了过去...

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,ans,sum,len[20];
string str[20];
bool vis[20],ok[20];
vector<int>G[20];
void dfs(int now,int step)
{
    if(ans==sum) return ;
    ans=max(ans,step);
    for(int i=0;i<G[now].size();i++)
        if(!vis[G[now][i]])
        {
            vis[G[now][i]]=true;
            dfs(G[now][i],step+len[G[now][i]]);
            vis[G[now][i]]=false;
        }
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>str[i];
        sum+=(len[i]=str[i].length());
    }
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(i!=j&&str[i][len[i]-1]==str[j][0])
            {
                ok[i]=ok[j]=true;
                G[i].push_back(j);
            }
    for(int i=0;i<n;i++) if(!ok[i]) sum-=len[i];
    for(int i=0;i<n;i++)
    {
        if(!ok[i]) continue;
        vis[i]=true;
        dfs(i,len[i]);
        vis[i]=false;
        if(ans==sum) break;
    }
    cout<<ans;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/coder-Uranus/p/9759877.html
今日推荐