C++OJ题经验总结(竞赛)3

注意:本篇标红字段均是可纳为己用的经验条。
OJ题知识归属:

1、第一题:动态规划 -> 区间dp(基于区间的左右端点,分情况讨论)

2、第二题:动态规划 -> 区间dp(基于区间的左右端点,分情况讨论)

3、第三题:动态规划 -> 区间dp(基于区间上某⼀点,划分成左右区间讨论)(线形)

4、第四题:动态规划 -> 区间dp(基于区间上某⼀点,划分成左右区间讨论)(环形)
OJ题来源:洛谷

OJ题名:回文字串

OJ题归属:动态规划【区间dp】

解题算法:动态规划

经验总结:区间dp本质可以使用背包那种棋盘式填表顺序,但是由于特判比较多,所以一般使用区间dp特有的填表顺序(第一层:循环子串长度 1 -> n;第二层:循环左端点;右端点可以计算出来)。

此题的五板斧:

cpp 复制代码
#include<iostream>
#include<string>

using namespace std;

const int N = 1010;

string s;
int n;
int f[N][N];

int main()
{
	cin >> s;
	n = s.size();
	s = " " + s;

	for (int len = 1; len <= n; len++)
	{
		for (int left = 1; left + len - 1 <= n; left++)
		{
			int right = left + len - 1;
			if (s[left] == s[right]) f[left][right] = f[left + 1][right - 1];
			else f[left][right] = min(f[left + 1][right], f[left][right - 1]) + 1;
		}
	}

	cout << f[1][n] << endl;

	return 0;
}

OJ题来源:洛谷

OJ题名:Treats for the Cows(款待奶牛)

OJ题归属:动态规划【区间dp】

解题算法:动态规划

经验总结:区间dp本质特点之一:就是只对区间的左右两头端点进行操作。

cpp 复制代码
#include<iostream>

using namespace std;

const int N = 2010;

int n;
int a[N];
int f[N][N];

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];

	for (int len = 1; len <= n; len++)
	{
		for (int i = 1; i + len - 1 <= n; i++)
		{
			int j = i + len - 1;
			f[i][j] = max(f[i + 1][j] + a[i] * (n - len + 1), f[i][j - 1] + a[j] * (n - len + 1));
		}
	}

	cout << f[1][n] << endl;

	return 0;
}

OJ题来源:洛谷

OJ题名:石子合并(弱化版)

OJ题归属:动态规划【区间dp】

解题算法:动态规划 / 小小空间优化的动态规划

五板斧:

cpp 复制代码
#include<iostream>
#include<cstring>

using namespace std;

const int N = 310;

int n;
int a[N];
int sum[N];
int f[N][N];

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		sum[i] = sum[i - 1] + a[i];
	}

	memset(f, 0x3f3f3f3f, sizeof f);
	for (int i = 0; i <= n; i++) f[i][i] = 0;

	for (int len = 2; len <= n; len++)
	{
		for (int i = 1; i + len - 1 <= n; i++)
		{
			int j = i + len - 1;
			int t = sum[j] - sum[i - 1];

			for (int k = i; k < j; k++)
			{
				f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + t);
			}
		}
	}

	cout << f[1][n] << endl;

	return 0;
}

//小小空间优化一下
#include<iostream>
#include<cstring>

using namespace std;

const int N = 310;

int n;
int sum[N];
int f[N][N];

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int x; cin >> x;
		sum[i] = sum[i - 1] + x;
	}

	memset(f, 0x3f3f3f3f, sizeof f);
	for (int i = 0; i <= n; i++) f[i][i] = 0;

	for (int len = 2; len <= n; len++)
	{
		for (int i = 1; i + len - 1 <= n; i++)
		{
			int j = i + len - 1;
			int t = sum[j] - sum[i - 1];

			for (int k = i; k < j; k++)
			{
				f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + t);
			}
		}
	}

	cout << f[1][n] << endl;

	return 0;
}

OJ题来源:洛谷

OJ题名:石子合并

OJ题归属:动态规划【区间dp】

解题算法:"倍增"/复写 + 动态规划

经验总结:遇到环形的区间dp,可以通过 "倍增"/复写 将环形数据变成直线型数据;比如:环形数据:4、5、9、4 -> 线形数据:4、5、9、4、4、5、9、4。

五板斧:

cpp 复制代码
#include<iostream>
#include<cstring>

using namespace std;

const int N = 210;

int n, m;
int s[N];
int f[N][N]; //min
int g[N][N]; //max

int main()
{
	cin >> n;
	m = 2 * n;
	for (int i = 1; i <= n; i++)
	{
		cin >> s[i];

		// "倍增"/复写
		s[i + n] = s[i];
	}

	//前缀和
	for (int i = 1; i <= m; i++) s[i] = s[i - 1] + s[i];

	//dp
	memset(f, 0x3f3f3f3f, sizeof f);
	memset(g, -0x3f3f3f3f, sizeof g);
	for (int i = 1; i <= m; i++) f[i][i] = g[i][i] = 0;

	for (int len = 1; len <= n; len++)
	{
		for (int i = 1; i + len - 1 <= m; i++)
		{
			int j = i + len - 1;
			int t = s[j] - s[i - 1];
			//枚举分割点
			for (int k = i; k < j; k++)
			{
				f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + t);
				g[i][j] = max(g[i][j], g[i][k] + g[k + 1][j] + t);
			}
		}
	}

	int ret1 = 0x3f3f3f3f, ret2 = -0x3f3f3f3f;
	//枚举 环->线 可能情况的左端点
	for (int i = 1; i <= n; i++)
	{
		ret1 = min(ret1, f[i][i + n - 1]);
		ret2 = max(ret2, g[i][i + n - 1]);
	}

	cout << ret1 << endl << ret2 << endl;

	return 0;
}
相关推荐
cao233313 分钟前
关于 y0 y1 yn 变量名冲突说法的指正
笔记
zyl7655 小时前
FlashAttention 详解:从 GPU 硬件、普通 Attention 到分块计算与 Online Softmax
算法
爱读书的小胖9 小时前
LeetCode 122:买卖股票的最佳时机 II(C)解题分析
算法
结局无敌9 小时前
React Native for OpenHarmony 实战:三方库 react-native-modal-dialog 的鸿蒙化适配指南
深度学习·算法·react native·react.js·harmonyos
WJ.Polar9 小时前
Java封装、继承、多态
java·开发语言
摇滚侠9 小时前
Java 中如何表示负数,有符号和无符号的区别,补码的计算规则,char byte short int long 的范围
java·开发语言
~patience~9 小时前
WAV 逆向分析工具使用笔记
笔记
滴啦嘟啦哒9 小时前
【具身笔记】【π0.5 微调与仿真】五、在MuJoCo中实现可视化仿真
笔记·python·vla·具身
OPEN-F9 小时前
ROS2系列教程:上位机建图与导航(原理综合)
算法·机器人
weixin_307779139 小时前
C++代码实现MATLAB中的fitrtree函数功能
开发语言·c++·算法·matlab