您的位置:首页 > 其它

CodeForces 939F Cutlet(dp+单调队列优化)

2018-03-16 00:32 381 查看
4000

F. Cutlet
time limit per test:4
seconds
memory limit per test:256
megabytes
input:standard
input
output:standard
output

Arkady wants tohave a dinner. He has just returned from a shop where he has bought asemifinished cutlet. He only needs to fry it. The cutlet should be fried for
2n seconds, in particular,
it should be fried for n
seconds on one side and n
seconds on the other side. Arkady hasalready got a frying pan and turn on fire, but understood that maybe he won'tbe able to flip the cutlet exactly after
n seconds afterthe
beginning of cooking.

Arkady is toobusy with sorting sticker packs in his favorite messenger and can flip thecutlet only in some periods of time. Namely, there are
k periods of time
in which he can do it, the i-th
of them isan interval of time from li
seconds afterhe starts cooking till ri
seconds,inclusive. Arkady decided that it's not required to flip the cutlet exactly inthe middle of cooking, instead, he will flip it several times in such a waythat the cutlet will be fried exactly
n seconds on oneside
and n seconds on
the other side in total.

Help Arkady andfind out if it's possible for him to cook the cutlet, if he is able to flip thecutlet only in given periods of time; and if yes, find the minimum number
offlips he needs to cook the cutlet.
Input

The first linecontains two integers
n and
k (1 ≤ n ≤ 100 000,
1 ≤ k ≤ 100) — the
number of seconds the cutlet should be cooked on each sideand number of periods of time in which Arkady can flip it.

The next
k lines contain descriptions
of these intervals. Each line contains twointegers li
and ri
(0 ≤ li ≤ ri ≤ 2·n),
meaning that Arkady can flip the cutlet in any moment starting from li
seconds after the beginning of cooking and finishing at ri
seconds after beginning of cooking. In particular, if li = ri
then Arkady can flip the cutlet only in the moment li = ri.
It's guaranteed that li > ri - 1
for all 2 ≤ i ≤ k.
Output

Output "Hungry"
if Arkadywon't be able to fry the cutlet for exactly n
seconds on oneside and exactly n
seconds on the other side.
Otherwise, output "Full"
in thefirst line, and the minimum number of times he should flip the cutlet in thesecond line.
Examples
Input
Copy

10 2

3 5

11 13
Output

Full

2
Input
Copy

10 3

3 5

9 10

11 13
Output

Full

1
Input
Copy

20 1

3 19
Output

Hungry
Note

In the firstexample Arkady should flip the cutlet in time moment
3 seconds afterhe starts cooking
and in time moment 13 seconds
after he starts cooking.

In the secondexample, Arkady can flip the cutlet at
10 seconds afterhe starts cooking.

        好久没有见过这种单调dp,差点连单调队列都不会了……
      大致题意,给以一块做鸡扒或者牛扒的肉,然后告诉这块肉要两面都煎恰好n分钟才能够熟,并且生的或者焦的都不行。但是我只能在特定的时间区间内把肉翻面。现在问是否能够吃到肉,如果可以并求出最少需要翻几次面。

       首先,本题状态的表示就已经是一个难点了。这个怎么想到的,其实我也不知道。dp[i][j]表示总共用时i分钟,当前不在煎的面煎了j分钟的最小翻面次数。对于朴素的dp,我们很容易想到dp[i][j]=min(dp[i-1][i-j]+1,dp[i-1][j]),前者表示在前一个时刻煎的是另一个面(对应反面的时间就是i-j),在当前时刻翻面;后者对应从前一个时刻到当前时刻没有翻面。可以看到,按照这样一个dp进行转移,可以求出正确答案。但是呢,这样子的时空复杂度都是O(N^2),对于1e5的数据来说显然时空上都不行。
      考虑优化,经过观察我们可以发现,在一个时间区间内,最多只需要翻转两次,以上则一定不是最优的。我们可以这么考虑,在进入这段区间的时候,我要么是在煎a面,要么是b面,对应出来也是如此。所以煎的情况可以分为两种:第一,以时间区间中某个点为分界线,之前煎a面,之后煎b面,反之也可;第二,时间区间被分成三个部分,第一和第三区间都是同一个面,然后中间那个是b面,反之也可。如果翻面次数大于2,那么我们可以换一下煎面的区间,最后总能够归到以上两种情况中,但是翻面次数更少。
        证明完这个定理之后,首先就可以优化空间,因为只能够在时间区间内翻面,而且区间内也只能翻面有限次,所以状态的表示可以改成dp[i][j],表示考虑到第i个时间区间,当前不在煎的面煎的时间为j的最小翻面次数。可得状态转移方程,dp[i][j]=min(dp[i-1][j-k]+2)和dp[i][j]=min(dp[i-1][ri-j-k]+1)。前者表示在第i个时间区间内翻面两次的情况,其中k为所说的中间部分的长度,意思是翻面两次,进出该时间区间的面是一样的,不同的是另一面多煎了k;后者表示在第i个时间区间内翻面一次的情况,其中k表示翻面点的位置,即进入的面在这个区间内煎了k分钟,其中ri-j-k表示进入该区间之时不在煎的面煎的时间。

       大致方法就是这样,对于dp状态的理解是关键,第二维的j表示当前不在煎的面,至于具体是哪个面,会随着时间变化。在理解的时候动笔记录,不要弄晕了。到了目前的这个地步,空间复杂度降下来了,如果用上滚动数组,空间复杂度可降到O(N)。但这个时间复杂度貌似是O(KN^2)的,但是仔细观察可知转移方程是单调的,可以用单调队列优化到O(NK)。当然了用线段树之流求区间最小,优化到O(KNlogN)也不是不可以。具体见代码(单调队列):
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define N 200010

using namespace std;

int dp[2]
,q
,n,k;

int main()
{
memset(dp,INF,sizeof(dp));
cin>>n>>k; dp[0][0]=0; int pre=0,cur=1;
for(int i=1;i<=k;i++)
{
int h=1,t=0,l,r;
scanf("%d%d",&l,&r);
for(int j=0;j<=r;j++)
{
if (j<=n)
{
while(h<=t&&dp[pre][j]<=dp[pre][q[t]]) t--;
q[++t]=j;
}
while(h<=t&&q[h]<j-(r-l)) h++;
dp[cur][j]=min(dp[pre][j],dp[pre][q[h]]+2);
}
h=1,t=0;
for(int j=r;j>=0;j--)
{
if (r-j<=n)
{
while(h<=t&&dp[pre][r-j]<=dp[pre][q[t]]) t--;
q[++t]=r-j;
}
while(h<=t&&q[h]<l-j) h++;
dp[cur][j]=min(dp[cur][j],dp[pre][q[h]]+1);
}
swap(cur,pre);
}
if (dp[pre]
>=INF) puts("Hungry");
else printf("Full\n%d",dp[pre]
);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: