题目传送门

  传送门

题目大意

  有一个位置数列,给定$n$条线索,每条线索从某一个位置开始,一直向左或者向右走,每遇到一个还没有在线索中出现的数就将它加入线索,问最小的可能的数列长度。

  依次从左到右考虑每一位上填的数。

  用$f_{L, a, R, b, S}$表示正在满足向右走的线索是$L$,前$a$个字符已经满足,正在满足向左走的线索是$R$,前$b$个字符还没有满足,还未被考虑的线索集合是$S$。

  主要有两种转移:

  • 填下一个字符

    • 如果两个线索下一个要填的字符相同,那么直接填
    • 如果不同则还需判断一下是否会使得另一线索不满足条件。
  • 更换线索
    • 向右走的线索是一堆类似于后缀的东西,向左走的线索是一堆类似于前缀的东西
    • 能不能在某个串的某个位置处更换某个串可以预处理出来

  loj上加了一堆常数优化卡到rk 1,估计很快就被超了。

  记得Doggu指着Claris这道题的非记忆化搜索写法给我说以后见着Claris记着%。

Code

 /**
* loj
* Problem#6037
* Accepted
* Time: 1224ms
* Memory: 25208k
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <set>
using namespace std;
typedef bool boolean; const int N = ;
const int Lim = << ; #define last_one(__x) (__builtin_ffs(__x) - 1) int n;
int len[N];
int s[N][N];
int exi[N][N];
int can[N][N]; // L: forward, R: backward
int usable[N][N];
int f[N][N][N][N][]; inline void init() {
scanf("%d", &n);
set<int> ss;
for (int i = , x; i < n; i++) {
int l = , hash_val = ;
while (~scanf("%d", &x) && x) {
s[i][l++] = x;
hash_val = hash_val * + x;
}
len[i] = l, s[i][l] = ;
if (ss.count(hash_val))
n--, i--;
else
ss.insert(hash_val);
} for (int i = ; i < n; i++)
for (int j = ; j < len[i]; j++)
exi[i][j + ] = exi[i][j] | ( << s[i][j]);
} // start at pos
boolean check(int a, int pos, int b) {
int *pa = s[a] + pos, *pb = s[b];
while (*pa || *pb) {
if (*pa == *pb)
pa++, pb++;
else if (( << *pb) & exi[a][pa - s[a]])
pb++;
else
return false;
}
return true;
} void upd(int& a, int b) {
if (a > b)
a = b;
} // considering s[L][pl], s[R][pr - 1], S remained
int dp(int L, int pl, int R, int pr, int S) {
if (!S && pl == len[L] && !pr)
return ;
int &rt = f[L][pl][R][pr][S];
if (rt)
return rt;
rt = Lim; for (int T = S & can[L][pl], i = last_one(T); T; T -= (T & (-T)), i = last_one(T))
upd(rt, dp(i, , R, pr, S ^ ( << i)));
if (!pr) {
for (int i = ; i < n && (S >> i); i++)
if ((S >> i) & )
// for (int j = 0; j <= len[i]; j++)
// if ((can[i][j] >> R) & 1)
// upd(rt, dp(L, pl, i, j, S ^ (1 << i)));
for (int T = usable[R][i], j = last_one(T); T; T -= (T & (-T)), j = last_one(T))
upd(rt, dp(L, pl, i, j, S ^ ( << i)));
} if (pl < len[L] || pr) {
int vl = s[L][pl], vr = ((pr) ? (s[R][pr - ]) : ());
if (vl == vr)
upd(rt, dp(L, pl + , R, pr - , S) + );
// if (pl < len[L] && _exi[R][pr] & (1 << vl))
if (pl < len[L] && exi[R][pr] & ( << vl))
upd(rt, dp(L, pl + , R, pr, S) + );
if (pr && exi[L][pl] & ( << vr))
upd(rt, dp(L, pl, R, pr - , S) + );
}
// cerr << L << " " << pl << " " << R << " " << pr << " " << S << " " << rt << '\n';
return rt;
} inline void solve() {
// forward
for (int idx = ; idx < n; idx++) {
for (int pos = ; pos <= len[idx]; pos++) {
for (int ano = ; ano < n; ano++) {
if (ano ^ idx)
can[idx][pos] |= check(idx, pos, ano) << ano;
}
// cerr << can[idx][pos] << ' ';
}
}
for (int i = ; i < n; i++) {
for (int j = ; j < n; j++) {
if (i ^ j) {
for (int pos = ; pos <= len[j]; pos++)
if ((can[j][pos] >> i) & )
usable[i][j] |= ( << pos);
}
}
}
len[n] = ;
for (int i = ; i < N; i++) {
exi[n][i] = ; //_exi[n][i] = 2046;
can[n][i] = ;
}
int all = ( << n) - , ans = Lim;
// ans = dp(n, 0, 1, len[1], all ^ 2);
for (int i = ; i < n; i++) {
upd(ans, dp(n, , i, len[i], all ^ ( << i)));
}
if (ans == Lim)
puts("-1");
else
printf("%d\n", ans);
} int main() {
init();
solve();
return ;
}

loj 6037 「雅礼集训 2017 Day4」猜数列 - 动态规划的更多相关文章

  1. LOJ #6037.「雅礼集训 2017 Day4」猜数列 状压dp

    这个题的搜索可以打到48分…… #include <cstdio> #include <cstring> #include <algorithm> ; bool m ...

  2. Loj 6036 「雅礼集训 2017 Day4」编码 - 2-sat

    题目传送门 唯一的传送门 题目大意 给定$n$个串,每个串只包含 ' .问是否可能任意两个不同的串不满足一个是另一个的前缀. 2-sat的是显然的. 枚举每个通配符填0还是1,然后插入Trie树. 对 ...

  3. 2018.10.27 loj#6035. 「雅礼集训 2017 Day4」洗衣服(贪心+堆)

    传送门 显然的贪心题啊...考试没调出来10pts滚了妙的一啊 直接分别用堆贪心出洗完第iii件衣服需要的最少时间和晾完第iii件衣服需要的最少时间. 我们设第一个算出来的数组是aaa,第二个是bbb ...

  4. LOJ#6035. 「雅礼集训 2017 Day4」洗衣服

    传送门 先处理出每一件衣服最早什么时候洗完,堆+贪心即可 然后同样处理出每件衣服最早什么时候烘干 然后倒序相加取最大值 # include <bits/stdc++.h> using na ...

  5. LOJ #6035.「雅礼集训 2017 Day4」洗衣服 贪心

    这道题的贪心好迷啊~我们对于两个过程进行单独贪心,然后再翻转一个,把这两个拼起来.先说一下单独贪心,单独贪心的话就是用一个堆,每次取出最小的,并且把这个最小的加上他单次的,再放进去.这样,我们得到的结 ...

  6. LOJ #6036.「雅礼集训 2017 Day4」编码 Trie树上2-sat

    记得之前做过几道2-sat裸体,以及几道2-sat前缀优化建图,这道题使用了前缀树上前缀树优化建图.我们暴力建图肯定是n^2级别的,那么我们要是想让边数少点,就得使用一些骚操作.我们观察我们的限制条件 ...

  7. [LOJ 6031]「雅礼集训 2017 Day1」字符串

    [LOJ 6031] 「雅礼集训 2017 Day1」字符串 题意 给定一个长度为 \(n\) 的字符串 \(s\), \(m\) 对 \((l_i,r_i)\), 回答 \(q\) 个询问. 每个询 ...

  8. [LOJ 6030]「雅礼集训 2017 Day1」矩阵

    [LOJ 6030] 「雅礼集训 2017 Day1」矩阵 题意 给定一个 \(n\times n\) 的 01 矩阵, 每次操作可以将一行转置后赋值给某一列, 问最少几次操作能让矩阵全为 1. 无解 ...

  9. [LOJ 6029]「雅礼集训 2017 Day1」市场

    [LOJ 6029] 「雅礼集训 2017 Day1」市场 题意 给定一个长度为 \(n\) 的数列(从 \(0\) 开始标号), 要求执行 \(q\) 次操作, 每次操作为如下四种操作之一: 1 l ...

随机推荐

  1. MySQL使用root权限创建用户并授权

    MySql篇 1.下载并安装Mysql (1)下载地址 MySQL-8.0下载地址 (2)Mysql配置 1.home目录下命令行执行:vi    .bash_profile来配置MySql绝对路径 ...

  2. 解决git pull/push每次都需要输入密码问题 和 HttpRequestException encountered

    如果我们git clone的下载代码的时候是连接的https://而不是git@git (ssh)的形式,当我们操作git pull/push到远程的时候,总是提示我们输入账号和密码才能操作成功,频繁 ...

  3. webpack(3)-管理资源

    管理资源:(file-loader 和 url-loader 可以接收并加载任何文件,然后将其输出到构建目录) 加载css:style-loader.css-loader 以style的形式插入到he ...

  4. btcpool之GbtMaker

    一.简介 GbtMaker全称getblocktemplate maker,它通过getblocktemplate rpc接口从bitcoind获得挖矿所需数据,然后把该数据发送到kafka消息队列. ...

  5. Jvisualvm 添加插件

    1.访问地址:https://visualvm.github.io/pluginscenters.html,找到自己JDK版本对应的插件下载地址(我的JDK版本为1.7.0_67): 2.点击该链接进 ...

  6. IO多路复用之select,poll,epoll个人理解

    在看这三个东西之前,先从宏观的角度去看一下,他们的上一个范畴(阻塞IO和非阻塞IO和IO多路复用) 阻塞IO:套接口阻塞(connect的过程是阻塞的).套接口都是阻塞的. 应用程序进程-----re ...

  7. VPS安全配置

    VPS安全配置 购买VPS后重装操作系统,以windows server 2008为例. 1 登录服务器 重装操作系统后,第一时间登录服务器,迅速进行安全配置. VPN-->Remote Des ...

  8. curl 支持 http2

    让 curl 支持 HTTP2 我们需要安装 nghttp2(http2 的 C 语言库) 源码安装 安装 nghttp2 git clone https://github.com/tatsuhiro ...

  9. andorid开发build.gradle 增加几种产品的方法

    因为需要有些ndk的开发,cmakelists里需要定义不同的变量,这个在网上搜索解决方案,不是很容易就直接找到答案. 尝试了不少,最后找到解决方案.升级Gradle3.0,这个文章价值高. 在ras ...

  10. FB面经prepare: Count the number of Vector

    给一个超级大的排好序的vector [abbcccdddeeee]比如,要求返回[{,a}, {,b}, {,c}, {,d}, {,e}......]复杂度要优于O(N) 分析: 如果是binary ...