【CF939F】Cutlet(单调队列优化DP)
- 有 \(0\sim 2n\) 这 \(2n+1\) 个时刻,共 \(2n\) 个时间间隔。
- 你需要烤一块肉,使得有恰好 \(n\) 个时间间隔烤正面,\(n\) 个时间间隔烤反面。
- 给定 \(k\) 个无交区间,表示可以将肉翻面的时刻。
- 求最小的翻面次数。
- \(1\le n\le10^5\),\(0\le k\le100\)
单调队列优化 DP
把能翻面的看作一种区间,不能翻面的看作另一种区间。设 \(f_{i,j,0/1}\) 表示到第 \(i\) 个区间为止正面烤了 \(j\) 个时间间隔,当前在烤正(\(0\))/反(\(1\))面,这一状态的最小翻面次数。
无论哪种区间我们都可以选择不翻面,如果第三维为 \(0\) 就给第二维加上区间长度,如果第三维为 \(1\) 就不变第二维,进行转移。
而对于能翻面的区间我们至多翻面两次,决定这个区间过后烤的那一面是否改变。
假设区间长度为 \(len\)。
如果翻面一次,\(f_{i,j,0}\) 可以从 \(f_{i-1,j-len+1\sim j,1}\) 转移,\(f_{i,j,1}\) 可以从 \(f_{i-1,j-len\sim j-1,0}\) 转移。
如果翻面两次,\(f_{i,j,0/1}\) 可以从 \(f_{i-1,j-len+1\sim j-1,1/0}\) 转移。
代码:\(O(nk)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100000
#define K 100
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,k,q[N+5],f[2*K+5][N+5][2];struct S {int l,r,op;}s[2*K+5];
int main()
{
RI i,j,c=0,x,y,lst=0;for(scanf("%d%d",&n,&k),i=1;i<=k;++i) scanf("%d%d",&x,&y),
!x&&++x,x<=y&&(x>lst+1&&(s[++c]=(S){lst+1,x-1,0},0),s[++c]=(S){x,lst=y,1},0);lst<2*n&&(s[++c]=(S){lst+1,2*n,0},0);//记录区间
RI H,T;for(i=0;i<=c;++i) for(j=0;j<=n;++j) f[i][j][0]=f[i][j][1]=1e9;for(f[0][0][0]=0,i=1;i<=c;++i)
{
for(j=0;j<=n;++j) j+s[i].r-s[i].l+1<=n&&Gmin(f[i][j+s[i].r-s[i].l+1][0],f[i-1][j][0]),Gmin(f[i][j][1],f[i-1][j][1]);//不翻面
if(!s[i].op) continue;
for(H=1,T=0,j=0;j<=n;++j) {W(H<=T&&q[H]<=j-(s[i].r-s[i].l+1)) ++H;W(H<=T&&f[i-1][j][1]<f[i-1][q[T]][1]) --T;q[++T]=j,Gmin(f[i][j][0],f[i-1][q[H]][1]+1);}//1->0
for(q[H=T=1]=0,j=1;j<=n;++j) {W(H<=T&&q[H]<j-(s[i].r-s[i].l+1)) ++H;W(H<=T&&f[i-1][j][0]<f[i-1][q[T]][0]) --T;Gmin(f[i][j][1],f[i-1][q[H]][0]+1),q[++T]=j;}//0->1
if(s[i].l==s[i].r) continue;
for(q[H=T=1]=0,j=1;j<=n;++j) {W(H<=T&&q[H]<=j-(s[i].r-s[i].l+1)) ++H;W(H<=T&&f[i-1][j][0]<f[i-1][q[T]][0]) --T;Gmin(f[i][j][0],f[i-1][q[H]][0]+2),q[++T]=j;}//0->1->0
for(q[H=T=1]=0,j=1;j<=n;++j) {W(H<=T&&q[H]<=j-(s[i].r-s[i].l+1)) ++H;W(H<=T&&f[i-1][j][1]<f[i-1][q[T]][1]) --T;Gmin(f[i][j][1],f[i-1][q[H]][1]+2),q[++T]=j;}//1->0->1
}return min(f[c][n][0],f[c][n][1])==1e9?puts("Hungry"):printf("Full\n%d\n",min(f[c][n][0],f[c][n][1])),0;
}
待到再迷茫时回头望,所有脚印会发出光芒