【动态规划】P8591 『JROI-8』颅脑损伤 2.0|普及+

本文涉及知识点

C++动态规划

P8591 『JROI-8』颅脑损伤 2.0

题目描述

给定 n n n 条线段,第 i i i 条是 [ l i , r i ] [l_i,r_i] [li,ri]。将每一条线段染成红色或黑色,要求:

  1. 任意两条红色线段不相交。
  2. 任意一条黑色线段至少和一条红色线段相交。

请最小化红色线段的长度和,并输出这个长度和。

一条线段 [ l i , r i ] [l_i,r_i] [li,ri] 的长度定义为 r i − l i r_i-l_i ri−li,两条线段 [ l i , r i ] , [ l j , r j ] [l_i,r_i],[l_j,r_j] [li,ri],[lj,rj] 交当且仅当 l i ≤ r j l_i\le r_j li≤rj 且 l j ≤ r i l_j\le r_i lj≤ri。

输入格式

第一行一行一个正整数,代表 n n n。

接下来 n n n 行,每行两个整数,代表 l i , r i l_i,r_i li,ri,用空格隔开。

输出格式

一行一个非负整数,代表红色线段的长度和的最小值。

输入输出样例 #1

输入 #1

复制代码
5
-6 5
1 3
-4 9
-1 10
6 8

输出 #1

复制代码
4

说明/提示

数据范围

测试点编号 n ≤ n\le n≤
1 ∼ 4 1\sim4 1∼4 10 10 10
5 ∼ 8 5\sim8 5∼8 400 400 400
9 ∼ 20 9\sim20 9∼20 3000 3000 3000

对于所有数据,满足 − 10 9 ≤ l i < r i ≤ 10 9 -10^9\le l_i<r_i\le10^9 −109≤li<ri≤109。

P8591 动态规划

将线段按左边界排序。

动态规划的状态表示

dp[i]表示第i条线段红色,且0 \\sim i 条线段都合法的最小长度和。

动态规划的填表顺序

性质一 :任何合法方案必定有一个红色线段,否则黑色线段无法和红色线段相邻。

先枚举只有一条线段是红色。

第一层循环:枚举第i条线段是红色。

第二层循环:枚举第j条线段是红色。且i,j之间的线段全黑。
时间复杂度:O(nn)

动态规划的转移方程

MinSelf(dp[j] , dp[i] + (j-i))

限制条件:一,i,j不相交。二, k ∈ ( i , j ) k\in(i,j) k∈(i,j)线段k和i或j相交。
性质二 :k1>j,线段k和k1相交,则一定和线段j相交。因为r_k \> l_{k1},l_j \< l_{k1} \\rightarrow r_k \> l_j
性质三 :k1 < i,k1和k相交,k和i不相交,则会漏掉。但k1,i,j是红色,劣于k1,j是红色。故漏掉,不会影响最终结果。

j1是第一条和i不相交的线,j必须和j相交。

动态规划的初始值

dp全部是LLONG_MAX/2。

先枚举只有一条线段是红色。 下标比i小的全部和i相交,则dp[i] = 线段i的长度。

动态规划的返回值

min(dp[i]) 线段i和任何下标大于i的线段都相交。

代码

核心代码

cpp 复制代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	cin >> n;
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}
template<class T = int>
vector<T> ReadNotNum() {
	vector<T> ret;
	T tmp;
	while (cin >> tmp) {
		ret.emplace_back(tmp);
		if ('\n' == cin.get()) { break; }
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
		AuotToFile();
	}
	void writestr(const char* sz) {
		strcpy(m_p, sz);
		m_p += strlen(sz);
		AuotToFile();
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
		AuotToFile();
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
		m_p = puffer;
	}
	~COutBuff() {
		ToFile();
	}
private:
	inline void AuotToFile() {
		if (m_p - puffer > N - 100) {
			ToFile();
		}
	}
	char  puffer[N], * m_p;
};

template<int N = 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {}
	inline CInBuff<N>& operator>>(char& ch) {
		FileToBuf();
		while (('\r' == *S) || ('\n' == *S) || (' ' == *S)) { S++; }//忽略空格和回车
		ch = *S++;
		return *this;
	}
	inline CInBuff<N>& operator>>(int& val) {
		FileToBuf();
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行		
		return *this;
	}
	inline CInBuff& operator>>(long long& val) {
		FileToBuf();
		long long x(0); int f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行
		return *this;
	}
	template<class T1, class T2>
	inline CInBuff& operator>>(pair<T1, T2>& val) {
		*this >> val.first >> val.second;
		return *this;
	}
	template<class T1, class T2, class T3>
	inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
		return *this;
	}
	template<class T1, class T2, class T3, class T4>
	inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
		return *this;
	}
	template<class T = int>
	inline CInBuff& operator>>(vector<T>& val) {
		int n;
		*this >> n;
		val.resize(n);
		for (int i = 0; i < n; i++) {
			*this >> val[i];
		}
		return *this;
	}
	template<class T = int>
	vector<T> Read(int n) {
		vector<T> ret(n);
		for (int i = 0; i < n; i++) {
			*this >> ret[i];
		}
		return ret;
	}
	template<class T = int>
	vector<T> Read() {
		vector<T> ret;
		*this >> ret;
		return ret;
	}
private:
	inline void FileToBuf() {
		const int canRead = m_iWritePos - (S - buffer);
		if (canRead >= 100) { return; }
		if (m_bFinish) { return; }
		for (int i = 0; i < canRead; i++)
		{
			buffer[i] = S[i];//memcpy出错			
		}
		m_iWritePos = canRead;
		buffer[m_iWritePos] = 0;
		S = buffer;
		int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
		if (readCnt <= 0) { m_bFinish = true; return; }
		m_iWritePos += readCnt;
		buffer[m_iWritePos] = 0;
		S = buffer;
	}
	int m_iWritePos = 0; bool m_bFinish = false;
	char buffer[N + 10], * S = buffer;
};

class Solution {
public:
	long long Ans(vector<pair<int, int>>& lrs) {
		const int N = lrs.size();
		sort(lrs.begin(), lrs.end());
		vector<long long > dp(N, LLONG_MAX / 2);
		auto Dis = [&](pair<int, int>& lr) {
			return (long long)lr.second - lr.first;
		};
		int iMinR = lrs[0].second;
		for (int i = 0; (i < N) && (iMinR >= lrs[i].first); i++) {
			dp[i] = Dis(lrs[i]);
			iMinR = min(iMinR, lrs[i].second);
		}
		for (int i = 0; i < N; i++) {
			int j1 = i;
			while ((j1 < N) && (lrs[j1].first <= lrs[i].second)) { j1++; }
			if (N == j1) { continue; }
			int iMinR = lrs[j1].second;
			for (int j = j1; j < N; j++) {
				if (iMinR < lrs[j].first) { break; }
				dp[j] = min(dp[j], dp[i] + Dis(lrs[j]));
				iMinR = min(iMinR, lrs[j].second);
			}
		}
		long long ans = LLONG_MAX / 2;
		for (int i = 0; i < N; i++) {
			if (lrs[i].second >= lrs.back().first) {
				ans = min(ans, dp[i]);
			}
		}
		return ans;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	//ios::sync_with_stdio(0); cin.tie(nullptr);
	CInBuff<> in; COutBuff<10'000'000> ob;
	auto lrs = Read<pair<int, int>>();
#ifdef _DEBUG		
	//printf("N=%d", N);
	Out(lrs, "lrs=");
	//Out(que, "que=");
	//Out(B, "B=");
	//Out(que, "que=");
	//Out(B, "B=");
#endif // DEBUG	
	auto res = Solution().Ans(lrs);
	cout << res << '\n';

	return 0;
}

单元测试

cpp 复制代码
vector<pair<int, int>> lrs;
		TEST_METHOD(TestMethod11)
		{
			lrs = { {-6,5},{1,3},{-4,9},{-1,10},{6,8} };
			auto res = Solution().Ans(lrs);
			AssertEx(4LL, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关推荐
王璐WL1 小时前
【C++】string的经典算法题
开发语言·c++·算法
阿贵---2 小时前
C++中的工厂模式高级应用
开发语言·c++·算法
倾心琴心2 小时前
【agent辅助pcb routing coding学习】实践7 length matching 算法学习
学习·算法·agent·pcb·routing
y = xⁿ2 小时前
【LeetCodehot100】T114:二叉树展开为链表 T105:从前序与中序遍历构造二叉树
java·算法·链表
灰色小旋风2 小时前
力扣20有效的括号(C++)
c++·算法·leetcode·职场和发展
逆境不可逃2 小时前
LeetCode 热题 100 之 160. 相交链表 206. 反转链表 234. 回文链表 141. 环形链表 142. 环形链表 II
算法·leetcode·链表
weiabc2 小时前
今日C/C++学习笔记20260223
c语言·c++·学习
CoovallyAIHub2 小时前
AAAI 2026 | 华中科大联合清华等提出Anomagic:跨模态提示零样本异常生成+万级AnomVerse数据集(附代码)
深度学习·算法·计算机视觉
悲伤小伞2 小时前
9-MySQL_索引
linux·数据库·c++·mysql·centos