Codeforces 939F Cutlet

版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/82927512

Problem

CodeForces

大意就是一个长度为2n的01序列,给定k个不相交的区间,在区间内的元素允许与前一个元素不同,代价为1,要求使得01序列中0和1恰好是n个,问最小的代价。

Solution

可以设出这样的一个dp,f[i][j]表示到第i段结束,当前没烤的这面烤j分钟的最小翻面次数。如果你觉得这个状态不那么方便你也可以加一维0/1咯。

有这么一个性质,在一段区间内只有不翻/翻1次/翻2次的三种情况,分别考虑一下。为了方便表达,我们把上一次的状态用0表示。

不翻,决策就是00000

f [ i ] [ j ] = f [ i 1 ] [ j ] f[i][j]=f[i-1][j]

翻一次,那么应该是形如00111的决策,要注意这个转移中的反面与上次的反面并不是一面。枚举ri时的反面又多烤多少分钟,即后面连续1的长度:

f [ i ] [ j ] = min k [ 0 , l r + 1 ] ( f [ i 1 ] [ r i j k ] ) + 1 f[i][j]=\min_{k\in[0,l-r+1]}(f[i-1][r_i-j-k])+1

这个好像可以用单调队列优化,但由于前驱状态的是-j,所以你可以把单调队列反着滑或者反着枚举k。

翻两次,形如00110的决策,枚举中间烤多少分钟,即中间连续1的长度:

f [ i ] [ j ] = min k [ 0 , l r ] ( f [ i 1 ] [ j k ] ) + 2 f[i][j]=\min_{k\in[0,l-r]}(f[i-1][j-k])+2

这个也可以用单调队列优化。

最后再把数组滚动一下即可。时间复杂度 O ( n k ) O(nk)

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;
}

猜你喜欢

转载自blog.csdn.net/As_A_Kid/article/details/82927512