首页 > 其他分享 >CF939F Cutlet 题解

CF939F Cutlet 题解

时间:2022-12-16 22:22:06浏览次数:40  
标签:idx min int 题解 Cutlet INF CF939F dp op

题意简述

有一个正反面都为 \(0\) 的卡片,每过 \(1\) 分朝下那一面的数值就会增加 \(1\),你可以在几个区间的时间内翻转卡片,求经过 \(2n\) 秒后能否让这个卡片的正反面的数都为 \(n\),求最小翻转数。

暴力

为了简单起见,我们定义一面为正面,一面为反面,\(1\) 表示正面,\(0\) 表示反面。

一眼看出来 dp,\(dp_{i,j,1/0}\) 表示在第 \(i\) 个区间结束后,正面数字为 \(j\),\(1/0\) 面朝上的最小翻转数。

那么状态转移方程为:

\(dp_{i,j,1}=\min(dp_{i-1,j-p,1}+2,dp_{i-1,j-q,0}+1,dp_{i-1,j-g,1})\)

\(dp_{i,j,0}=\min(dp_{i-1,j-p,1}+1,dp_{i-1,j-q,0}+2,dp_{i-1,j,0})\)

其中满足:

区间间隔 \(\le p \le\) 两个区间右端点间隔。

区间长度 \(\le q \le\) 两个区间右端点间隔。

\(g\) 表示两个区间右端点间隔。

不难发现,这样的转移是 \(O(n)\) 的,再加上我们需要计算 \(nk\) 个 dp 值,这样做显然超时。需要考虑优化。

优化

不难发现,在计算一个区间完成后的值时,如果我们从 \(n\) 到 \(0\) 来计算,每次计算值所需要遍历区间的上限和下限都时逐渐减少的,那么我们就可以采取单调队列优化了。

这里用到一个小技巧:

如果一开始遍历区间的下限不为最低,那么我们可以令一个变量 \(idx\) 一开始指向第一个区间的下限,然后逐渐降低,分批入队。

这个单调队列可以滚动掉一维,只需要保留它记录 dp 值的 \(0/1\) 就行了。

Code

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, K = 110, INF = 0x3f3f3f3f;

int dp[K][N * 2][2];
int g[K], n, k, idx[2];

deque <int> q[2];

struct line {
	int l, r;
	bool operator < (const line& rhs) const {
		return l < rhs.l;
	}
}lines[K];

inline void q_push (int i, int j, int op) {
	while (q[op].size() && dp[i][q[op].back()][op] >= dp[i][j][op]) q[op].pop_back();
	q[op].push_back(j);
} 

inline void q_pop (int i, int j, int op) {
	while (q[op].size() && j - q[op].front() < 0) q[op].pop_front();
}

inline void init () {
	q[1].clear(); q[0].clear();
	idx[0] = idx[1] = 2 * n;
}

int main () {
	memset(dp, INF, sizeof(dp));
	scanf("%d%d", &n, &k);
	for (int i = 1;i <= k;i++) scanf("%d%d", &lines[i].l, &lines[i].r);
	sort(lines + 1, lines + 1 + k);
	g[1] = lines[1].l; g[k + 1] = 2 * n - lines[k].r;
	for (int i = 2;i <= k;i++) g[i] = lines[i].l - lines[i - 1].r;
	for (int j = lines[1].l;j <= lines[1].r;j++) {
		dp[1][j][1] = 2;
		dp[1][j][0] = 1;
	}
	dp[1][lines[1].r][1] = 0;
	for (int i = 2;i <= k;i++) {
		int l = lines[i].r - lines[i - 1].r, lg = lines[i].r - lines[i].l;
		init();
		for (int j = lines[i].r;j >= 0;j--) {
			while (j - l <= idx[1] && idx[1] > 0) q_push(i - 1, idx[1]--, 1);
			while (j - lg <= idx[0] && idx[0] > 0) q_push(i - 1, idx[0]--, 0);
			q_pop(i - 1, j - g[i], 1); q_pop(i - 1, j, 0);
			dp[i][j][1] = min(dp[i][j][1], (q[1].size() ? dp[i - 1][q[1].front()][1] + 2 : INF));
			dp[i][j][1] = min(dp[i][j][1], (q[0].size() ? dp[i - 1][q[0].front()][0] + 1 : INF));
			dp[i][j][1] = min(dp[i][j][1], (j - l >= 0 ? dp[i - 1][j - l][1] : INF));
//			cout << i << " " << j << " 1: " << dp[i][j][1] << endl;
		}
		init();
		for (int j = lines[i].r;j >= 0;j--) {
			while (j - l <= idx[1] && idx[1] > 0) q_push(i - 1, idx[1]--, 1);
			while (j - lg <= idx[0] && idx[0] > 0) q_push(i - 1, idx[0]--, 0);
			q_pop(i - 1, j - g[i], 1); q_pop(i - 1, j, 0);
			dp[i][j][0] = min(dp[i][j][0], (q[1].size() ? dp[i - 1][q[1].front()][1] + 1 : INF));
			dp[i][j][0] = min(dp[i][j][0], (q[0].size() ? dp[i - 1][q[0].front()][0] + 2 : INF));
			dp[i][j][0] = min(dp[i][j][0], dp[i - 1][j][0]);
//			cout << i << " " << j << " 0: " << dp[i][j][0] << endl; 
		}
	}
	if (min(n - g[k + 1] >= 0 ? dp[k][n - g[k + 1]][1] : INF, dp[k][n][0]) < INF) printf("Full\n%d\n", min(n - g[k + 1] >= 0 ? dp[k][n - g[k + 1]][1] : INF, dp[k][n][0]));
	else printf("Hungry\n");
	return 0;
} 

标签:idx,min,int,题解,Cutlet,INF,CF939F,dp,op
From: https://www.cnblogs.com/zxh-mc/p/16988410.html

相关文章

  • YACS 11 月甲题解
    https://iai.sh.cn/contest这把还是简单的,难度对标普及组。所有题解均口胡。T1观察&性质你扫左端点,然后维护以当前左端点最长的合法子段,显然右端点单不降,因为当你......
  • 【LeetCode】题解合集(JavaScript版)
    前言:今年断断续续写了些LeetCode题目,一方面是为了一个比较现实的问题——面试,最重要的是要复习之前学习的数据结构与算法。后面有时间还会接续刷题…题解合集:题号题目题解1......
  • NOIP2022 题解
    终于有机会补NOIP的题了T1考虑枚举C与F的纵列考虑预处理出每个点最左边和最下边可以延伸到哪之后枚举列,然后对行做类似于扫描线的操作,统计有多少可行的"第一横行"......
  • NOIP2022 题解
    T2T3......
  • CF1408G 题解
    题意传送门给定\(n\)个点的带权无向完全图,点\(i,j\)之间的权值为\(a_{i,j}\),权值是一个\(1\sim\frac{n(n-1)}{2}\)的排列。计数把原图划分成\(k\)个组的方......
  • TeraTerm 日文乱码问题解决
    windows中文系统上,用TeraTerm连接日文服务器,经常会出现乱码的问题。本篇是本地win11系统,TeraTerm安装时选择英文。Generalsetup依次选择Setup->General.........
  • LeetCode 题解 2487. 从链表中移除节点
    2487.从链表中移除节点-力扣(Leetcode)题解思路一:递归逆序structListNode*removeNodes(structListNode*head){if(head->next==NULL)//遍历到链表末尾......
  • 问题解决系列:IDEA引入@Slf4j使用log变量,编译之后报log cannot be resolved
    问题场景IDEA引入@Slf4j使用log变量,编译之后报logcannotberesolved。本篇博客主要是针对此种情况进行解决。问题环境软件版本JDK1.8问题原因主要会有以下几方面的问题:未......
  • VNC常用操作及常见问题解决办法汇总
     VNC登录用户缺省是root,但在安装oracle时必须用oracle用户的身份登录,下面我们就以oracle为例说明如何配置VNC,从而可以使用不同的用户登录到主机。步骤描述如下:   步骤......
  • NOIP 2022 题解(个人)
    \(T1\)种花可以维护每一个点向下最多延伸多长\(xia_i\),向右延伸最多多长\(you_i\),这样C就好求了,可以维护\(you_i\)一个自下而上的后缀和。至于F就维护一个\(x......