UPC6902 Trie树(状压DP)

6902: Trie树

时间限制: 1 Sec  内存限制: 128 MB
提交: 176  解决: 37
[提交] [状态] [讨论版] [命题人:admin]

题目描述

字母(Trie)树是一个表示一个字符串集合中所有字符串的前缀的数据结构,其有如下特征:
1.树的每一条边表示字母表中的一个字母
2.树根表示一个空的前缀
3.树上所有其他的节点都表示一个非空前缀,每一个节点表示的前缀为树
根到该节点的路径上所有字母依次连接而成的字符串。
4.一个节点的所有出边(节点到儿子节点的边)中不存在重复的字母。

 《UPC6902 Trie树(状压DP)》

单词“A”“to”“tea”“ted”“ten”“i”“in”“inn”对应的Trie树

现在Matej手上有N个英文小写字母组成的单词,他想知道,如果将这N个单词中的字母分别进行重新排列,形成的字母树的节点数最少是多少。

 

输入

第一行包含一个正整数N(1<=N<=16)
接下来N行每行一个单词,每个单词都由小写字母组成。
单词的总长度不超过1,000,000。

 

 

输出

输出仅一个正整数表示N个单词经过重新排列后,字母树的最少节点数。

 

样例输入

3
a
ab
abc

 

样例输出

4

 

如果是两个单词,那么这俩单词相同字母尽量多的时候那么构成的树的节点数是最少的,但是如果多个单词出现以上结论就不成立,很容易就证明,于是如果单词书多余两个的时候,将其分成若干个两两相交的状态,从而由小集合推到大集合。

 

那么如何枚举状态呢 那么需要用到位运算 即:for(j = i;j;j = (j-1)&i)。

 

最后DP部分,根据一开始的分析,很容易得到状态转移,找到所有串的公共部分sum,用最后枚举得到的dp[i]跟sum做差,得到当前的最优状态。状态需要从小的集合到大的集合过渡,只有子结构最优的前提下,才能得到最优的当前状态,然后考虑dp[i] == sum的时候,说明此时的所有选择的串都相同,也就不存在需要减去的情况,故只有当dp[i] > sum时,才需要dp[i]-sum。

最后需要结果+1.字典树的根节点是空值。收获:高效枚举子状态for(j = i;j;j = (j-1)&i)

 

以上题解来自大佬的讲解:历史上最丑的男人

 

 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define IO ios::sync_with_stdio(false),cin.tie(0)
#define FIN freopen("D://code//in.txt", "r", stdin)
#define ppr(i,x,n) for(int i = x;i <= n;i++)
#define rpp(i,n,x) for(int i = n;i >= x;i--)
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
 
inline int read() {//读入挂
    int ret = 0, c, f = 1;
    for(c = getchar(); !(isdigit(c) || c == '-'); c = getchar());
    if(c == '-') f = -1, c = getchar();
    for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
    if(f < 0) ret = -ret;
    return ret;
}
int dp[maxn],vis[30][30],len[maxn];
int main()
{
    int n;
    IO;
    cin>>n;
    int R = 1<<n;
    string s;
    ppr(i,0,n-1)
    {
        cin>>s;len[i]=s.size();
        ppr(j,0,s.size()-1){vis[i][s[j]-'a']++;}
    }
    ppr(i,0,R-1)
    {
        int sum = 0;
        ppr(j,0,n-1){if((1<<j)&i) dp[i] += len[j];}
        ppr(j,0,25)
        {
            int minn = inf;
            ppr(k,0,n-1)
            {
                if((1<<k)&i)
                minn = min(minn,vis[k][j]);  
            }
			sum += minn;
        }
        for(int j = i;j;j = (j-1)&i)
        {
            dp[i] = min(dp[i],dp[j]+dp[i^j]);
        }
        if(dp[i] > sum) dp[i] -= sum;
    }
    ll ans = dp[R-1]+1;
    cout<<ans<<endl;    
}

 

    原文作者:Trie树
    原文地址: https://blog.csdn.net/Pandapan1997/article/details/81298063
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞