好神奇的dp...

首先有一个很简单的思想:设dp[i][j]表示目前到了第i分钟,朝上的面被烤了j分钟的情况下所需的最小交换次数

那么有转移:dp[i][j]=min(dp[i-1][j],dp[i-1][i-j]+1)

这一点很好理解,就是讨论现在向上这面上一分钟的状态:如果上一分钟这一面也朝上,那么就直接继承,如果上一分钟这一面朝下,那么就要翻一次,同时之前朝上的面的被烤的时间就是i-j

但这个转移显然是O(n^2)的,这样做过不去这道题。

所以我们考虑优化

可是,这个转移在某种意义上已经达到了最优,所以不太好优化了...

那么我们只能重构状态

我们发现,能对肉进行翻面的时间只有这k个给定的时间段,且k<=100,那么我们考虑:能否把一个时间段整体更新,将时间复杂度降成O(nk)呢?

这样我们必须重新设计状态:记状态dp[i][j]表示目前到了第i个时间段的结尾,朝上这一面的肉被烤的时间为j

那么我们就要重新考虑转移:

首先我们观察一下规律:

可以发现:在一段可以翻转的区间内,真正翻转的次数只可能有0,1或2三种,所以我们只需按这三种情况分别讨论转移即可

(关于上面这句话的解释:如果你翻转了三次或更多,那么你一定可以将某几次翻转合并后变成上述情况,也就是说上述情况就足够覆盖所有状态)

翻转次数为0的情况很好判断,直接继承上一次的状态即可

如果翻转次数为2,那么这相当于原来朝上的面现在还朝上,原来朝下的面现在还朝下,只是中途把朝上的面翻过去烤了一下而已。

那么我们可以枚举在之前朝上的面在这一段时间内被烤的时间k,那么在这段区间之前这一面被烤的之间就是j-k

于是可以进行转移:dp[i][j]=min(dp[i-1][j-k]+2)

但是这样做显然时间不够优越,所以我们要采取策略来优化!

我们推一发式子:

,那么:

发现这样的形式就可以用单调队列维护了。

从小向大枚举j,然后推进单调队列中维护即可

至于翻转次数为1的情况,相当于现在朝上的面原先朝下,那么如果现在朝上的面被烤的时间为j,枚举现在朝下的面在这一区间内被烤的时间为k,那么r-j就是现在朝下的面被烤的时间,而我们枚举了朝下的面在这一区间内被烤的时间为k,那么在进入这一区间之前现在朝下的面原来朝上,已经被烤过的时间就是r-j-k!

这样有转移:dp[i][j]=min(dp[i-1][r-j-k])+1!

用类似上述的方法,同样构造单调队列转移,注意此时要倒序枚举,因为我们枚举的东西事实上起到的是j+k的作用,所以r-枚举的东西就要倒序

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
int dp[2][1000005];
int que[1000005];
int n,k;
int main()
{
scanf("%d%d",&n,&k);
int now=1,past=0;
memset(dp[past],0x3f,sizeof(dp[past]));
dp[0][0]=0;
while(k--)
{
memcpy(dp[now],dp[past],sizeof(dp[now]));
int l,r;
scanf("%d%d",&l,&r);
int head=1,tail=0;
for(int i=0;i<=min(n,r);i++)
{
while(head<=tail&&dp[past][que[tail]]>=dp[past][i])
{
tail--;
}
que[++tail]=i;
while(head<=tail&&que[head]<i-(r-l))
{
head++;
}
dp[now][i]=min(dp[now][i],dp[past][que[head]]+2);
}
head=1,tail=0;
for(int i=r;i>=0;i--)
{
while(head<=tail&&dp[past][que[tail]]>=dp[past][r-i])
{
tail--;
}
while(head<=tail&&que[head]<l-i)
{
head++;
}
que[++tail]=r-i;
dp[now][i]=min(dp[now][i],dp[past][que[head]]+1);
}
swap(now,past);
}
if(dp[past][n]>=0x3f3f3f3f)
{
printf("Hungry\n");
}else
{
printf("Full\n%d\n",dp[past][n]);
}
return 0;
}

CF939F的更多相关文章

  1. CF939F Cutlet (单调队列优化DP)

    题目大意:要煎一块有两个面的肉,只能在一段k不相交的时间段$[l_{i},r_{i}]$内翻转,求$2*n$秒后,保证两个面煎的时间一样长时,需要最少的翻转次数,$n<=100000$,$k&l ...

随机推荐

  1. javascript面试--网络收集

    史上最全的Javascript面试题总结(内附答案) - CSDN博客http://blog.csdn.net/u011277123/article/details/70208768 6.什么是未声明 ...

  2. python - psutil 系统信息模块

    # .psutil是一个跨平台库能够轻松实现获取系统运行的进程和系统利用率(包括CPU.内存.磁盘.网络等)信息. # 它主要用来做系统监控,性能分析,进程管理. # 它实现了同等命令行工具提供的功能 ...

  3. shell编程 之 运算符

    1 shell运算符简介 Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符 形如:val=`expr 2 + 2`:echo "两数之和为 : $val"    ...

  4. Netty实现简单UDP服务器

    本文参考<Netty权威指南> 文件列表: ├── ChineseProverbClientHandler.java ├── ChineseProverbClient.java ├── C ...

  5. 【转】python模块分析之typing(三)

    [转]python模块分析之typing(三) 前言:很多人在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果,就不得不去阅读代码的具体内容,降低了阅读的速度 ...

  6. CTex+WinEdt10.2安装详细教程与破解

    原文地址:https://www.cnblogs.com/xiachongkun/p/8176390.html Latex作为目前最好用的文档编排工具,以前只是简单会一点,现在也已经忘得差不多了.因为 ...

  7. js声明引入和变量声明和变量类型、变量

    问题: 在网页的发展历程中,发现网页不能对用户的数据进行自动校验,和提供一些特效. 解决: 使用javascript. 作用 可以让网页和用户进行直接简单的交互. 可以让网页制作特效和动画. 声明js ...

  8. ansible笔记(6):常用模块之命令类模块

    ansible笔记():常用模块之命令类模块 command模块 command模块可以帮助我们在远程主机上执行命令 注意:使用command模块在远程主机中执行命令时,不会经过远程主机的shell处 ...

  9. zabbix3.2监控redis

    redis的监控 .监控脚本 # vim /usr/local/zabbix_agents_3.2.0/scripts/redismonitor.sh #! /bin/bash #Name: redi ...

  10. python操作三大主流数据库(13)python操作redis之新闻项目实战①新闻数据的导入

    1.新闻处理页面redis_news.py #coding:utf-8 import math import redis class RedisNews(object): def __init__(s ...