动态规划中的2 P2051 [AHOI2009]中国象棋 [HNOI2003]消防局的设立

19 篇文章 0 订阅

动态规划中的2

在动态规划中我们经常会遇到关于2的问题,即往往不是0,1两种状态
比如:不能超过2
这个时候我们可以考虑将0,1,2设为状态
状态转移为0,1,2三种状态相互转移的公式

中国象棋的炮

题意

给出 n ∗ m n*m nm的棋盘,,计算在棋盘上放炮的方案,使得棋盘上的炮不能相互攻击的到

分析

状态

显然,每行每列可以放置的炮的数量不能超过2

若我们知道每一列0,1,2的数量,然后我们就能按行转移得到方案数

一个想法是,用状压DP,用三进制表示每一列的状态
但显然复杂度肯定超了

但我们仔细想想,并不需要知道每一列的状态,只需要知道0的列数,1的列数,2的列数
我们记录为1的列数和为2的列数即可

状态转移方程

d p [ k + 1 ] [ i ] [ j ] = ( d p [ k + 1 ] [ i ] [ j ] + d p [ k ] [ i ] [ j ] ) dp[k + 1][i][j] = (dp[k + 1][i][j] + dp[k][i][j]) % mod dp[k+1][i][j]=(dp[k+1][i][j]+dp[k][i][j])
d p [ k + 1 ] [ i + 1 ] [ j ] = ( d p [ k + 1 ] [ i + 1 ] [ j ] + d p [ k ] [ i ] [ j ] ∗ ( m − i − j ) ) dp[k + 1][i + 1][j] = (dp[k + 1][i + 1][j] + dp[k][i][j] * (m - i - j)) % mod dp[k+1][i+1][j]=(dp[k+1][i+1][j]+dp[k][i][j](mij))
d p [ k + 1 ] [ i − 1 ] [ j + 1 ] = ( d p [ k + 1 ] [ i − 1 ] [ j + 1 ] + d p [ k ] [ i ] [ j ] ∗ i ) dp[k + 1][i - 1][j + 1] = (dp[k + 1][i - 1][j + 1] + dp[k][i][j] * i) % mod dp[k+1][i1][j+1]=(dp[k+1][i1][j+1]+dp[k][i][j]i)
d p [ k + 1 ] [ i + 2 ] [ j ] = ( d p [ k + 1 ] [ i + 2 ] [ j ] + d p [ k ] [ i ] [ j ] ∗ ( m − i − j ) ∗ ( m − i − j − 1 ) / 2 ) dp[k + 1][i + 2][j] = (dp[k + 1][i + 2][j] + dp[k][i][j] * (m - i - j) * (m - i - j - 1) / 2) % mod dp[k+1][i+2][j]=(dp[k+1][i+2][j]+dp[k][i][j](mij)(mij1)/2)
d p [ k + 1 ] [ i ] [ j + 1 ] = ( d p [ k + 1 ] [ i ] [ j + 1 ] + d p [ k ] [ i ] [ j ] ∗ ( m − i − j ) ∗ i ) dp[k + 1][i][j + 1] = (dp[k + 1][i][j + 1] + dp[k][i][j] * (m - i - j) * i) % mod dp[k+1][i][j+1]=(dp[k+1][i][j+1]+dp[k][i][j](mij)i)
d p [ k + 1 ] [ i − 2 ] [ j + 2 ] = ( d p [ k + 1 ] [ i − 2 ] [ j + 2 ] + d p [ k ] [ i ] [ j ] ∗ i ∗ ( i − 1 ) dp[k + 1][i - 2][j + 2] = (dp[k + 1][i - 2][j + 2] + dp[k][i][j] * i * (i - 1) dp[k+1][i2][j+2]=(dp[k+1][i2][j+2]+dp[k][i][j]i(i1)

代码

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const LL mod = 9999973;
const int maxn = 105;
LL dp[maxn][maxn][maxn];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int n, m; LL sum = 0;
	cin >> n >> m;
	dp[0][0][0] = 1;
	for (int k = 0; k < n; k++) {
		for (int i = 0; i <= m; i++) {
			for (int j = 0; i + j <= m; j++) {
				dp[k + 1][i][j] = (dp[k + 1][i][j] + dp[k][i][j]) % mod;
				dp[k + 1][i + 1][j] = (dp[k + 1][i + 1][j] + dp[k][i][j] * (m - i - j)) % mod;
				dp[k + 1][i - 1][j + 1] = (dp[k + 1][i - 1][j + 1] + dp[k][i][j] * i) % mod;
				dp[k + 1][i + 2][j] = (dp[k + 1][i + 2][j] + dp[k][i][j] * (m - i - j) * (m - i - j - 1) / 2) % mod;
				dp[k + 1][i][j + 1] = (dp[k + 1][i][j + 1] + dp[k][i][j] * (m - i - j) * i) % mod;
				dp[k + 1][i - 2][j + 2] = (dp[k + 1][i - 2][j + 2] + dp[k][i][j] * i * (i - 1) / 2) % mod;
			}
		}
	}
	for (int i = 0; i <= m; i++)
		for (int j = 0; i + j <= m; j++)
			sum = (sum + dp[n][i][j]) % mod;
	cout << sum << '\n';
}

消防局的设立

题目描述

2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地。起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地A到基地B至少要经过d条道路的话,我们称基地A到基地B的距离为d。

由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过2的基地的火灾。

你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。

分析

这是一道经典的树形DP例题
一般就是给你一棵数和每个点可向外覆盖的点的个数,求最少选几个点可以覆盖所有的点
该类题状态为一般为:1.第几号点
2.还可以向外覆盖多少点
本题可以外覆2个点,递推过程中公有5个状态

状态

d p [ i ] [ 0 ] dp[i][0] dp[i][0]:覆盖其子树以及他本身、他的父亲以及他的父亲的父亲(爷爷?)
d p [ i ] [ 1 ] dp[i][1] dp[i][1]:覆盖其子树以及它本身以及他的父亲
d p [ i ] [ 2 ] dp[i][2] dp[i][2]:覆盖其子树以及他本身
d p [ i ] [ 3 ] dp[i][3]% dp[i][3]:覆盖其所有子数
d p [ i ] [ 4 ] dp[i][4] dp[i][4]:覆盖其所有儿子的子树

状态转移方程

i:表示当前节点 s:表示其儿子 k:表示其其他儿子
d p [ i ] [ 0 ] = s u m ( d p [ s ] [ 4 ] ) + 1 dp[i][0]=sum(dp[s][4])+1 dp[i][0]=sum(dp[s][4])+1
覆盖本身节点、其父亲以及爷爷需要在其本身设立新的消防站,则其儿子和孙子也已经被覆盖
则覆盖子树只需要将其孙子的子树都覆盖即可。也就是 s u m ( d p [ s ] [ 4 ] ) sum(dp[s][4]) sumdp[s][4]

d p [ i ] [ 1 ] = m i n ( d p [ s ] [ 0 ] + s u m ( d p [ k ] [ 3 ] ) , d p [ i ] [ 0 ] ) dp[i][1]=min(dp[s][0]+sum(dp[k][3]),dp[i][0]) dp[i][1]=min(dp[s][0]+sum(dp[k][3]),dp[i][0])
覆盖其父亲,只要有一个子树能覆盖到其爷爷,
其他子树的本身节点也可以由该子树覆盖,其他子树只需要覆盖到其儿子即可。

d p [ i ] [ 2 ] = m i n ( d p [ s ] [ 1 ] + s u m ( d p [ k ] [ 2 ] ) , d p [ i ] [ 1 ] ) dp[i][2]=min(dp[s][1]+sum(dp[k][2]),dp[i][1]) dp[i][2]=min(dp[s][1]+sum(dp[k][2]),dp[i][1])
覆盖其本身,只要有一个子树能覆盖到其父亲,
其他子树只需要覆盖到其本身即可。

d p [ i ] [ 3 ] = m i n ( s u m ( d p [ s ] [ 2 ] ) , d p [ i ] [ 2 ] ) dp[i][3]=min(sum(dp[s][2]),dp[i][2]) dp[i][3]=min(sum(dp[s][2]),dp[i][2])
覆盖到其儿子,只需要其所有子树覆盖到本身即可

d p [ i ] [ 4 ] = m i n ( s u m ( d p [ s ] [ 3 ] ) , d p [ i ] [ 3 ] ) dp[i][4]=min(sum(dp[s][3]),dp[i][3]) dp[i][4]=min(sum(dp[s][3]),dp[i][3])
覆盖到其孙子,只需要其所有子树覆盖到儿子即可

代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 1005;
const int inf = 0X3f3f3f3f;
int n;
vector<int> E[maxn];
int dp[maxn][5];
//0 为+2	dp[i][0]=sum(dp[s][4])
//1 为+1	dp[i][1]=min(dp[s][0]+sum(dp[k][3]),dp[i][0])
//2 为0		dp[i][2]=min(dp[s][1]+sum(dp[k][2]),dp[i][1])
//3 为-1	dp[i][3]=sum(dp[s][2])
//4 为-2	dp[i][4]=sum(dp[s][3])
void dfs(int x) {
	dp[x][0] = 1;
	dp[x][1] = dp[x][2] = dp[x][3] = dp[x][4] = 0;
	for (int i = 0; i < E[x].size(); i++) {
		dfs(E[x][i]);
		dp[x][0] += dp[E[x][i]][4];
		dp[x][3] += dp[E[x][i]][2];
		dp[x][4] += dp[E[x][i]][3];
	}
	if (E[x].size() == 0) {
		dp[x][1] = dp[x][2] = 1;
	}
	else{
		for (int i = 0; i < E[x].size(); i++) {
			dp[x][1] += dp[E[x][i]][3];
			dp[x][2] += dp[E[x][i]][2];
		}
		int f1 = inf, f2 = inf;
		for (int i = 0; i < E[x].size(); i++) {
			f1 = min(f1, dp[E[x][i]][0] - dp[E[x][i]][3]);
			f2 = min(f2, dp[E[x][i]][1] - dp[E[x][i]][2]);
		}
		dp[x][1] += f1;
		dp[x][2] += f2;
	}
	for (int i = 1; i <= 4; i++)
		dp[x][i] = min(dp[x][i], dp[x][i - 1]);
}
int main() {
	scanf("%d", &n);
	int f;
	for (int i = 2; i <= n; i++) {
		scanf("%d", &f);
		E[f].push_back(i);
	}
	dfs(1);
	printf("%d\n", dp[1][2]);
}
AHOI2001是一种用于处理模式匹配和字符串搜索的经典算法,全称为"Another Happy Odyssey in 2001"。它通常应用于构建高效、空间优化的KMP(Knuth-Morris-Pratt)算法的一种改进版本。这种有限自动机常用于处理字符串搜索问题,尤其是在处理大量文本数据时。 关于题目代码的具体内容,这通常涉及到编程竞赛或算法实现题。通常,你需要编写一段程序,包括定义一个有限状态机(Finite Automaton),处理输入字符串和模式串,并根据AHOI2001算法来查找模式是否在原字符串。关键部分会涉及如何创建前缀函数表、动态规划和自适应策略。 由于这不是一个直接的答案,下面是一个简化版的代码框架示例(假设用Python): ```python class AhoCorasickAutomaton: def __init__(self, patterns): self.prefix_func = self.build_prefix_function(patterns) def build_prefix_function(self, patterns): # 建立前缀函数表的计算过程... pass def search(self, text): index = 0 for pattern in patterns: while index < len(text) and index + len(pattern) <= len(text): if self.match(text[index:], pattern): return True index += self.prefix_func[pattern] return False def match(self, text, pattern): # 匹配函数,比较两个字符串是否相等... pass # 使用示例: patterns = ['AB', 'AC'] # 输入模式列表 automaton = AhoCorasickAutomaton(patterns) text = 'ABCABCD' # 待搜索的字符串 if automaton.search(text): print("Pattern found") else: print("Pattern not found")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值