[USACO15JAN]Moovie Mooving G
[USACO15JAN]Moovie Mooving G
状压难题。不过也好理解。
首先我们根据题意:
she does not want to ever visit the same movie twice.
这句话以及\(n\)的取值范围给了我们足够多的提醒:可能是状态压缩。
那么,当我们真正尝试用集合表示每个电影是否已看过的时候,我们却不容易给出能够转移的状态。
举个例子,当我们定义的状态中集合表示成为该状态的元素,那么这将会与最终求该集合的最小值冲突——状态中没法记录集合的最小值。
我们不妨换一种思路:\(dp(i,S)\)表示的是以\(i\)为结尾、已经看过的集合为\(S\)的最长时间。这种状态在转移的时候能够避免计算到不合法的状态,我们接下来具体分析:
容易得到:
\]
其中,\(query(dp(j,S\setminus\{i\}))\)代表的是电影\(i\)中时间节点不超过\(dp(j,S\setminus\{i\})\)的最大值。而该状态下允许不存在这样的时间节点,转移的时候特殊处理即可。
这是由于计算的时候避免出现间隔——即看电影时间处处连续。
最后,统计最终答案的时候对于所有的大于等于限制\(L\)的状态计算它们的集合大小,更新。
这样做的时间复杂度为\(O(n^22^nlogn)\)。实际测试的时候会更快。
……
int n, L, t, c[N], d[N], st[N][C], siz[S] = {}, ttt[S];
int ans = INF, dp[N][S];
int query(int cur, int x)
{
int L = 0, R = c[cur], mid, tmp;
while(L < R)
{
mid = L + ((R - L) >> 1);
tmp = st[cur][mid];
if(tmp == x) return x;
if(tmp < x) L = mid + 1;
else R = mid;
}
if(!R) return -1;
return st[cur][R - 1];
}
int main()
{
scanf("%d %d", &n, &L);
for(int i = 0; i < n; ++ i)
{
scanf("%d %d", &d[i], &c[i]);
for(int j = 0; j < c[i]; ++ j) scanf("%d", &st[i][j]);
}
t = (1 << n) - 1;
for(int i = 1; i <= t; ++ i) siz[i] = siz[i ^ (i & (-i))] + 1;
for(int i = 0; i < n; ++ i) ttt[1 << i] = i;
for(int i = 1; i <= t; ++ i) ttt[i] = ttt[i & (-i)];
memset(dp, 0, sizeof(dp));
for(int s = 1; s <= t; ++ s)
{
for(int s0 = s, i = ttt[s]; s0; s0 &= ~(1 << i), i = ttt[s0])
{
if(siz[s] == 1 && st[i][0] == 0) dp[i][s] = d[i];
else
{
for(int s1 = s ^ (1 << i), j = ttt[s1]; s1; s1 &= ~(1 << j), j = ttt[s1])
{
int tmp = query(i, dp[j][s ^ (1 << i)]);
dp[i][s] = max(dp[i][s], dp[j][s ^ (1 << i)]);
if(tmp != -1) dp[i][s] = max(dp[i][s], tmp + d[i]);
}
}
// printf("dp[%d][%d] = %d\n", i, s, dp[i][s]);
if(dp[i][s] >= L) ans = min(ans, siz[s]);
}
}
if(ans == INF) puts("-1");
else printf("%d\n", ans);
return 0;
}
其实,我们发现,在我们转移的时候,我们完全可以仅记录一个集合代表该集合下所能达到的最长时间结点。这样一来,我们只需要枚举集合中的每一个元素,直接转移即可。
这是因为第一维状态是无用的,因为转移只和DP出的值有关,与元素无关。
时间复杂度:\(O(n2^nlogn)\)。空间也可以承受的了。
……
int n, L, t, c[N], d[N], st[N][C], siz[S] = {}, ttt[S];
int ans = INF, dp[S];
int query(int cur, int x)
{
int L = 0, R = c[cur], mid, tmp;
while(L < R)
{
mid = L + ((R - L) >> 1);
tmp = st[cur][mid];
if(tmp == x) return x;
if(tmp < x) L = mid + 1;
else R = mid;
}
if(!R) return -1;
return st[cur][R - 1];
}
int main()
{
scanf("%d %d", &n, &L);
for(int i = 0; i < n; ++ i)
{
scanf("%d %d", &d[i], &c[i]);
for(int j = 0; j < c[i]; ++ j) scanf("%d", &st[i][j]);
}
t = (1 << n) - 1;
for(int i = 1; i <= t; ++ i) siz[i] = siz[i ^ (i & (-i))] + 1;
for(int i = 0; i < n; ++ i) ttt[1 << i] = i;
for(int i = 1; i <= t; ++ i) ttt[i] = ttt[i & (-i)];
memset(dp, 0, sizeof(dp));
for(int s = 1; s <= t; ++ s)
{
for(int s0 = s, i = ttt[s]; s0; s0 &= ~(1 << i), i = ttt[s0])
{
int tmp = query(i, dp[s ^ (1 << i)]);
dp[s] = max(dp[s], dp[s ^ (1 << i)]);
if(tmp != -1) dp[s] = max(dp[s], tmp + d[i]);
if(dp[s] >= L) ans = min(ans, siz[s]);
}
}
if(ans == INF) puts("-1");
else printf("%d\n", ans);
return 0;
}
总结:
- 这道题敢于在转移的过程中进行非\(O(1)\)做法的计算;
- 当转移与状态中集合元素本身无关联,与状态值有关时应去除冗杂信息。
[USACO15JAN]Moovie Mooving G的更多相关文章
- P3118 [USACO15JAN]Moovie Mooving G
P3118 [USACO15JAN]Moovie Mooving G Link 题目描述 Bessie is out at the movies. Being mischievous as alway ...
- Luogu3118:[USACO15JAN]Moovie Mooving
题面 传送门 Sol 设\(f[S]\)表示看过的电影集合为\(S\),当前电影的最大结束时间 枚举电影和电影的开始时间转移 可以对开始时间\(sort\) 二分一下转移即可 # include &l ...
- [USACO15JAN]电影移动Moovie Mooving
[USACO15JAN]电影移动Moovie Mooving 时间限制: 2 Sec 内存限制: 128 MB 题目描述 Bessie is out at the movies. Being mis ...
- P3119 [USACO15JAN]Grass Cownoisseur G
P3119 [USACO15JAN]Grass Cownoisseur G tarjan缩点+分层图上跑 spfa最长路 约翰有 \(n\) 块草场,编号 \(1\) 到 \(n\),这些草场由若干条 ...
- [bzoj3886] [USACO15JAN]电影移动Moovie Mooving
题目链接 状压\(dp\). 注意到\(n\leq 20\)且每个只能用一次,所以很显然可以压缩每部电影看过没,记\(f[sta]\)为状态为\(sta\)时最多可以看多久. 转移时先枚举状态,然后枚 ...
- BZOJ3886 : [Usaco2015 Jan]Moovie Mooving
f[i]表示用i集合内的电影可以达到的最长时间 f[i]向f[i|(1<<j)]更新,此时的时间为第j部电影在f[i]前的最晚上映时间 先排序一遍离散化后用前缀最大值解决 时间复杂度$O( ...
- 【bzoj3886】[Usaco2015 Jan]Moovie Mooving 状态压缩dp+二分
题目描述 Bessie is out at the movies. Being mischievous as always, she has decided to hide from Farmer J ...
- [Usaco2015 Jan]Moovie Mooving
Description Bessie is out at the movies. Being mischievous as always, she has decided to hide from F ...
- DP测试总结
T1:三取方格数 题目描述 设有N*N的方格图,我们将其中的某些方格填入正整数,而其他的方格中放入0.某人从图得左上角出发,可以向下走,也可以向右走,直到到达右下角.在走过的路上,他取走了方格中的数. ...
随机推荐
- 原生ajax分享
最近被大佬问了一个很有趣的问题,你还能手打出一个ajax吗?,我当时的想法是有现成的为什么要自己打,后来我反思了一下(只有靠自己才是强者),在这里给大家分享一个我自己打的ajax,也是自己的一个知识点 ...
- Kubernetes 存储简介
存储分类结构图 半持久化存储 1.EmptyDir EmptyDir是一个空目录,生命周期和所属的 Pod 是完全一致的,EmptyDir的用处是,可以在同一 Pod 内的不同容器之间共享工作过程中产 ...
- Service Mesh架构的持续演进 单体模块化 SOA 微服务 Service Mesh
架构不止-严选Service Mesh架构的持续演进 网易严选 王育松 严选技术团队 2019-11-25 前言同严选的业务一样,在下层承载它的IT系统架构一样要生存.呼吸.增长和发展,否则过时的.僵 ...
- python_3 装饰器之初次见面
装饰器 定义:本质是函数,(只不过是用来装饰其他函数而已),就是为其他函数添加附加功能 原则: 1. 不能修改被修饰函数的源代码 2.不能修改被修饰函数的调用方式 实现装饰器的知识储备 1.函数即&q ...
- C# 8.0 可空(Nullable)给ASP.NET Core带来的坑
Nullable reference types(可为空引用类型) 可为空引用类型不讲武德 C#8.0 引入了"可为空引用类型"和"不可为空引用类型",使我们能 ...
- jsaper子报表Subreport(父子报表互相传值)
有很多人都说Jasperreports不适合中国式复杂报表,实际上运用好父子报表可以解决大部分问题了.例如下面的表.每个学生的学科数目不固定,且每个学生后有相当于小计的平均分.有点复杂度的报表,可以使 ...
- CSP2020-S游记
写在前面 Q:按照惯例是不是要写一片游记啊? A:好像是吧.. Q:都爆零了还有脸写游记? A:再不写今年就没机会了啊 Q:-- 我不要脸 Day -?? 某二区学长毒奶:去年三棵树,今年六张图 Da ...
- 2020最新idea永久激活教程
注:以下为 idea 最新版本 2020.3.2 的永久激活教程,请按照如下步骤安装激活 (webstorm.pycharm 激活方法相同). 步骤一.下载安装包 去到 idea 官网 https:/ ...
- JavaScript 类型、原型与继承学习笔记
目录 一.概览 二.数据类型 1. JavaScript中的数据类型 2. 什么是基本类型(Primitive Data Type) 2.1 概念 2.2 七个基本类型 2.3 基本类型封装对象 3. ...
- CSS选择器,属性前缀,长度单位,变形效果,过渡效果,动画效果
CSS3选择器 ·*通配选择器 ·E标签选择器 ·E#id ID选择器 ·E.class类选择器 ·E F包含选择器,后代选择器 ·E>F子包含选择器 ·E+F相邻兄弟选择器 ·E[foo]属性 ...