版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/82927512
Problem
大意就是一个长度为2n的01序列,给定k个不相交的区间,在区间内的元素允许与前一个元素不同,代价为1,要求使得01序列中0和1恰好是n个,问最小的代价。
Solution
可以设出这样的一个dp,f[i][j]表示到第i段结束,当前没烤的这面烤j分钟的最小翻面次数。如果你觉得这个状态不那么方便你也可以加一维0/1咯。
有这么一个性质,在一段区间内只有不翻/翻1次/翻2次的三种情况,分别考虑一下。为了方便表达,我们把上一次的状态用0表示。
不翻,决策就是00000
翻一次,那么应该是形如00111的决策,要注意这个转移中的反面与上次的反面并不是一面。枚举ri时的反面又多烤多少分钟,即后面连续1的长度:
这个好像可以用单调队列优化,但由于前驱状态的是-j,所以你可以把单调队列反着滑或者反着枚举k。
翻两次,形如00110的决策,枚举中间烤多少分钟,即中间连续1的长度:
这个也可以用单调队列优化。
最后再把数组滚动一下即可。时间复杂度 。
Code
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=100010,INF=0x3f3f3f3f;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
struct data{
int l,r;
bool operator < (const data &t)const{return l<t.l;}
}a[maxn];
int n,k,len,f[2][maxn<<1];
deque<int> q;
void input()
{
read(n);read(k);
for(rg int i=1;i<=k;i++) read(a[i].l),read(a[i].r);
sort(a+1,a+k+1);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int pre=0,cur=1;
input();
for(rg int i=1;i<=n+n;i++) f[cur][i]=INF;
for(rg int i=1;i<=k;i++)
{
pre^=1;cur^=1;len=a[i].r-a[i].l+1;
while(!q.empty()) q.pop_front();
memmove(f[cur],f[pre],sizeof(f[cur]));
for(rg int j=a[i].r;~j;j--)
{
while(!q.empty()&&f[pre][q.back()]>=f[pre][a[i].r-j]) q.pop_back();
q.push_back(a[i].r-j);
while(!q.empty()&&q.front()<a[i].l-j) q.pop_front();
if(!q.empty()) getmin(f[cur][j],f[pre][q.front()]+1);
}
while(!q.empty()) q.pop_front();
q.push_back(0);
for(rg int j=0;j<=n&&j<=a[i].r;j++)
{
while(!q.empty()&&f[pre][q.back()]>=f[pre][j]) q.pop_back();
q.push_back(j);
while(!q.empty()&&q.front()<=j-len) q.pop_front();
if(!q.empty()) getmin(f[cur][j],f[pre][q.front()]+2);
}
}
if(f[cur][n]>=INF) puts("Hungry");
else printf("Full\n%d\n",f[cur][n]);
return 0;
}