P1278 单词游戏

P1278 单词游戏

一道比较基础的状压题,看到题解很多人都打的记搜,小蒟蒻来发个状压dp。

由于不能多次使用同一个单词,对于每个单词只有0(不选)1(选)两种情况,并且n只有16显然是状压了。

预处理

对于每个单词我们只关心首尾和长度,所以就记下每个单词的首尾和长度。

设计状态

这题最简单的点就是状态很好设:f[i][j] i为二进制串表示用了哪些单词(基本的状压操作)j表示末尾是哪个单词。

转移

j,k为两单词,当t[j]与h[k]相同时,f[i|(1<<(k-1))][k]=max(f[i][j]+l[k],f[i|(1<<(k-1))][k]);

细节

注意j一定是i状态中选了的词,k是没选过的,以及亿些位运算的细节比如卡了小蒟蒻1h的i|1<<(k-1)才是将i的从右往左第k位变成1。

AC代码

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
int h[20],t[20],l[20],f[200000][20],n,ans;
string s[20];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        l[i]=s[i].size();
        h[i]=int(s[i][0]);
        t[i]=int(s[i][l[i]-1]);
        f[(1<<(i-1))][i]=l[i];
    }
    for(int i=1;i<=(1<<n)-1;i++)///枚举状态 
    {
        for(int j=1;j<=n;j++)///枚举最后一个词 
        {
            if(!i&(1<<(j-1))||!f[i][j])continue;///细节 
            for(int k=1;k<=n;k++)///枚举加进来的词 
            {
                if(i&(1<<(k-1))||t[j]!=h[k])continue;///细节 
                f[i|(1<<(k-1))][k]=max(f[i][j]+l[k],f[i|(1<<(k-1))][k]);
            }
        }
    }
    for(int i=1;i<=(1<<n)-1;i++)
        for(int j=1;j<=n;j++)
            ans=max(ans,f[i][j]);
    cout<<ans; 
    return 0;
} 

 

虽然dp的效率比不过各种玄学优化的搜索,但dp还是很稳定的。

 

posted @ 2021-09-03 16:28  T_X蒻  阅读(68)  评论(0)    收藏  举报