好神奇的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. Django REST Framework API Guide 05

    本节大纲 1.Serializer fields 2.Serializer relations Serializer fields 1.serializer 字段定义在fields.py文件内 2.导 ...

  2. Linux 01 计算机系统硬件组成简介

    PC服务器 1U = 4.445cm 企业1-2U比较多 互联网公司,品牌 DELL,HP, IBM. Dell品牌 2010年之前:1850,1950(1u),2850,2950(2u) 2010年 ...

  3. html超文本标记语言基础一

    1,基本格式 <!DOCTYPE html> //声明为 HTML5 文档 <html> <head> <meta charset="utf-8&q ...

  4. ROI Pool和ROI Align

    这里说一下ROI Pool和ROI Align的区别: 一.ROI Pool层: 参考faster rcnn中的ROI Pool层,功能是将不同size的ROI区域映射到固定大小的feature ma ...

  5. 嵌入式系统C编程之堆栈回溯【转】

    转自:https://www.cnblogs.com/clover-toeic/p/3949896.html 前言 在嵌入式系统C语言开发调试过程中,常会遇到各类异常情况.一般可按需添加打印信息,以便 ...

  6. 看完此文还不懂NB-IoT,你就过来掐死我吧...

    看完此文还不懂NB-IoT,你就过来掐死我吧....... 1 1G-2G-3G-4G-5G 不解释,看图,看看NB-IoT在哪里? 2 NB-IoT标准化历程 3GPP NB-IoT的标准化始于20 ...

  7. python3+selenium入门12-警告框处理

    在WebDriver中要处理JS生成的alert.confirm以及prompt,需要使用到switch_to_alert()定位到alert/confirm/prompt,然后再使用text.acc ...

  8. struts2框架之拦截器(参考第二天学习笔记)

    拦截器 1. 什么是拦截器 1). 与JavaWeb中的Filter比较相似. 2). 拦截器只能拦截Action!!! 2. Struts中定义了很多拦截器,其中defaultStack中的拦截器会 ...

  9. 【转】RESTful Webservice创建

    RESTful Web Services with Java   REST stands for REpresentational State Transfer, was first introduc ...

  10. 026_nginx引用lua遇到的坑

    server { listen 80; listen 443 ssl; server_name www.jyall.com; access_log /data/log/nginx/*.www.jyal ...