前言

首先,看到这道题目,我首先想到的是暴搜,通过\(vector\)来搞,代码也是很短的。

这里用了一个类似于分治的思想

把一个大问题转化为小问题

先枚举第一个单词,之后把能拼接在它后面的单词都一个一个的去试,哪个最优选哪个

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T&x){
T f=1;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
x*=f;
}//快读,常数优化
template<typename T>inline void write(T x){
if(x<0){
putchar('-');
write(x*-1);
return;
}
if(x>9)write(x/10);
putchar(x%10+48);
}//快写,常数优化
string st[18];
vector<int>v[210];//动态数组
int f[18];//标记数组
int dfs(int x){
int ans=0;
for(auto i:v[st[x][st[x].size()-1]])//v数组是存第1个字母的一个容器
if(!f[i]){
f[i]=1;//标记这个字符串已经用过了
ans=max(ans,dfs(i));//打擂
f[i]=0;//回溯
}
return ans+st[x].size();
}
int main(){
int ans=0,n;
read(n);//读入
for(int i=1;i<=n;i++)cin>>st[i],v[st[i][0]].push_back(i);//读入,放入vector容器
for(int i=1;i<=n;i++){
f[i]=1;//表记
ans=max(ans,dfs(i));//打擂法找到最优解
f[i]=0;//回溯
}
write(ans);//输出
return 0;
}

然后,你会发现你只得了70分,开\(O(2)\)试试?TLEagain!

想一想更优秀的算法,加记忆化?是的!

正文

储存状态

如何存状态

我们发现每一个字符串的状态都要么是0,要么是1,所以我们可以用二进制的思想去压缩状态。

\[1≤N≤16
\]

\[2^{n(16)}=65536
\]

开数组很充裕,浪费也不要紧。

判断状态

如何去判断第\(i\)个单词有没有用过

从右往左这个数二进制的第\(i\)位是\(1\),就代表这个单词用过,反之\(0\)就代表这个单词没用过。

但给你这么一个数,你该这么去判断呢?

用位运算!

如果第\(i\)为是\(1\),那么\(x>>(i-1)\)后\(\mod2\)就是\(1\)

如果第\(i\)为是\(0\),那么\(x>>(i-1)\)后\(\mod2\)就是\(0\)

那么判断这个单词是否用过,我们就可以这么写

if(!((y>>(i-1)&1))//按位与,只有两个数这一位都是1才为1,所以只有当最后一位是1,这个数才会是1,否则是0

标记状态

如何将这一位变成\(1\)

将这一位变成\(1\),我们可以用位运算中的按位或——两位都是\(0\),这一位的得数才为\(0\)

y|(1<<(i-1))

这应该是很显然的

总结

现在就可以看总的代码了

#include <bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T&x){
T f=1;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
x*=f;
}
template<typename T>inline void write(T x){
if(x<0){
putchar('-');
write(x*-1);
return;
}
if(x>9)write(x/10);
putchar(x%10+48);
}
string st[18];
vector<int>v[210];
int f[17][1<<17];
int dfs(int x,int y){
if(f[x][y])return f[x][y];
int ans=0;
for(auto i:v[st[x][st[x].size()-1]])
if(!((y>>(i-1))&1))ans=max(ans,dfs(i,y|(1<<(i-1))));
return f[x][y]=ans+st[x].size();
}
int main(){
int ans=0,n;
read(n);
for(int i=1;i<=n;i++)cin>>st[i],v[st[i][0]].push_back(i);
for(int i=1;i<=n;i++)ans=max(ans,dfs(i,(1<<(i-1))));
write(ans);
return 0;
}

刚开始我认为这应该没有多少重复运算,所以我写了个暴搜,但是,我写了记忆化之后惊奇地发现,暴搜总用时\(4.00s\),也就是\(4000ms\),而记忆化搜索总用时\(73ms\),快了不只一点。但是空间确实消耗很大。

编程中有很多算法,用空间换时间,记忆化搜索就是这么一个代表,我们要学习这种思想,想出更巧妙的办法!

题解 P1278 【单词游戏】的更多相关文章

  1. 洛谷 P1278 单词游戏

    P1278 单词游戏 题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单 ...

  2. Luogu P1278 单词游戏(dfs)

    P1278 单词游戏 题意 题目描述 \(Io\)和\(Ao\)在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何 ...

  3. 洛谷 P1278 单词游戏 【状压dp】

    题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单词禁止说两遍,游戏中只 ...

  4. P1278 单词游戏

    题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单词禁止说两遍,游戏中只 ...

  5. [洛谷P1278]单词游戏

    题目大意:给一个有$n(n\leqslant16)$个单词的字典,求单词接龙的最大长度 题解:发现$n$很小,可以状压,令$f_{i,j}$表示选的数的状态为$i$,最后一个字母是$j$的最大长度. ...

  6. LUOGU 1278 单词游戏

    题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单词禁止说两遍,游戏中只 ...

  7. 【题解】JXOI2018游戏(组合数)

    [题解]JXOI2018游戏(组合数) 题目大意 对于\([l,r]\)中的数,你有一种操作,就是删除一个数及其所有倍数.问你删除所有数的所有方案的步数之和. 由于这里是简化题意,有一个东西没有提到: ...

  8. LOJ#10106. 「一本通 3.7 例 2」单词游戏

    题目链接:https://loj.ac/problem/10106 题目描述 来自 ICPC CERC 1999/2000,有改动. 有 NNN 个盘子,每个盘子上写着一个仅由小写字母组成的英文单词. ...

  9. codevs1580单词游戏

    题目描述中说: 单词为at,k=8则新单词为ib 推移规则是:如果k为正数则下推,否则上推,当推移超越边界时回到另一端继续推移. 但在我做题时发现: 这个描述与数据所要求的是完全相反的!!!! 样例1 ...

随机推荐

  1. Mybatis分页插件的使用流程

    如果你也在用Mybatis,建议尝试该分页插件,这一定是最方便使用的分页插件.该插件支持任何复杂的单表.多表分页. 1.引入PageHelper的jar包 在pom.xml中添加如下依赖: 12345 ...

  2. Book. Effective C++ item2-尽量使用const, enum, inline替换#define

    ##常规变量 c++里面的#define后面的定义部分,是不算代码的一部分的.所以如果你使用#define: #define ASPECT_RATIO 1.653 你希望这个代号ASPECT RATI ...

  3. datatables异步获取数据、简单实用

    IKC项目总结 一.认证难题管理模块 1. 如何使用datatables进行获取数据内容 datatables简介:Datatables是一款jquery表格插件.它是一个高度灵活的工具,可以将任何H ...

  4. JMeter-接口测试之数据驱动

    前言 之前我们的用例数据都是配置在Http 请求中,每次需要增加,修改用例都需要打开 jmeter 重新编辑,当用例越来越多的时候,用例维护起来就越来越麻烦,有没有好的方法来解决这种情况呢?我们可以将 ...

  5. vuex源码阅读分析

    这几天忙啊,有绝地求生要上分,英雄联盟新赛季需要上分,就懒着什么也没写,很惭愧.这个vuex,vue-router,vue的源码我半个月前就看的差不多了,但是懒,哈哈.下面是vuex的源码分析在分析源 ...

  6. 微信小程序最新授权方法,getUserInfo

    20180511微信小程序正式关闭原先getUserInfo的逻辑 不再允许自动弹出授权框. 方法一: index.wxml(准备一个用于给用户授权的页面,我这里直接用了一个全屏按钮) <vie ...

  7. 用ABAP 生成二维码 QR Code

    除了使用我的这篇blogStep by step to create QRCode in ABAP Webdynpro提到的使用ABAP webdynpro生成二维码之外,也可以通过使用二维码在线生成 ...

  8. 前端ps中常用的操作

    昨天,ui给了个psd图,让写成网页.额,要自己切图.很久之前,操作的还凑乎.但是,好久了,都忘了.所以,打算自己记个笔记,方便以后查看. 首先,打开ps就先来设置一下ps的单位啦点击最上面的一行的编 ...

  9. python——字符串截取

    str = ‘0123456789’ print str[0:3] #截取第一位到第三位的字符 print str[:] #截取字符串的全部字符 print str[6:] #截取第七个字符到结尾 p ...

  10. C++ non-const lvalue reference cannot bind to a temporary

    1. 问题代码 #include <iostream> #include <vector> //注意begin和end形参都声明为引用 bool find_int(std::v ...