【动态规划】codeforces1149B Three Religions

本文探讨了一种算法,用于判断三种宗教信仰描述是否能在宇宙之言中形成不相交的子序列,确保和平共存。通过预处理和动态规划策略,解决了宗教描述随时间演变的复杂问题。

B. Three Religions
time limit per test3 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
During the archaeological research in the Middle East you found the traces of three ancient religions: First religion, Second religion and Third religion. You compiled the information on the evolution of each of these beliefs, and you now wonder if the followers of each religion could coexist in peace.

The Word of Universe is a long word containing the lowercase English characters only. At each moment of time, each of the religion beliefs could be described by a word consisting of lowercase English characters.

The three religions can coexist in peace if their descriptions form disjoint subsequences of the Word of Universe. More formally, one can paint some of the characters of the Word of Universe in three colors: 1, 2, 3, so that each character is painted in at most one color, and the description of the i-th religion can be constructed from the Word of Universe by removing all characters that aren’t painted in color i.

The religions however evolve. In the beginning, each religion description is empty. Every once in a while, either a character is appended to the end of the description of a single religion, or the last character is dropped from the description. After each change, determine if the religions could coexist in peace.

Input
The first line of the input contains two integers n,q (1≤n≤100000, 1≤q≤1000) — the length of the Word of Universe and the number of religion evolutions, respectively. The following line contains the Word of Universe — a string of length n consisting of lowercase English characters.

Each of the following line describes a single evolution and is in one of the following formats:

  • i c (i∈{1,2,3}, c∈{a,b,…,z}: append the character c to the end of i-th religion description.
  • i (i∈{1,2,3}) – remove the last character from the i-th religion description. You can assume that the pattern is non-empty.
    You can assume that no religion will have description longer than 250 characters.

Output
Write q lines. The i-th of them should be YES if the religions could coexist in peace after the i-th evolution, or NO otherwise.

You can print each character in any case (either upper or lower).


 这题很是遗憾,其实自己思路都已经对了,但是因为一些愚蠢的小错误,一直在debug,导致比赛结束也没有AC,使得昨晚的Rating暴减,再次错过了黄名的机会。以后无论什么比赛都不能掉以轻心,思路一定要清晰,一定要清晰!
 首先看到这题,我们先回想一下一个简化的问题,如果只有唯一的宗教,判断是否是子串。很容易想到我们需要事先处理一个nxt[100005][26]nxt[100005][26]nxt[100005][26]数组,记录模板串中每一个位置中下一个字符的最小出现位置,倒着递推即可。然后找子串就成了一个不断在nxtnxtnxt中跳下标的事情,如果跳到结尾,说明没有这个串。
 回到这个问题,判断三个字符串面临的一个关键问题在于,是应该选择那一个宗教的下一个字符进行跳跃最优,事实证明贪心是不行的。注意到了每个宗教不超过250个字符,2503=15625000250^3=156250002503=15625000,很像是在做dp的数据范围。我们用dp[i][j][k]dp[i][j][k]dp[i][j][k]记录当前状态为第一个宗教的前i个字符,第二个宗教的前j个字符,第三个宗教的前k个字符,对应的下标最小值,那么它们的更新就是应该由dp[i−1][j][k],dp[i][j−1][k],dp[i][j][k−1]dp[i-1][j][k],dp[i][j-1][k],dp[i][j][k-1]dp[i1][j][k],dp[i][j1][k],dp[i][j][k1]递推而来,这是因为下标最小的状态对应的字符可能来自于第一,第二,第三个宗教。
 然后就不用多说了,来一个字符,就把涉及到的状态dp一次,最终需要比较dp[a[1].size()−1][a[2].size()−1][a[3].size()−1]dp[a[1].size()-1][a[2].size()-1][a[3].size()-1]dp[a[1].size()1][a[2].size()1][a[3].size()1]和n的大小即可。
 上代码!

#include<cstdio>
#include<string>
using namespace std;

string a[4];
char s[200005],o[2],tmp[2];
int nxt[200005][26],n,q,i,pos,g[4],dp[252][252][252];

int main()
{
	scanf("%d%d%s",&n,&q,s+1);
	a[1]=a[2]=a[3]="0";
	for(int i=0;i<26;i++)
		nxt[n][i]=nxt[n+1][i]=n+1;
	for(int i=n-1;i>=0;i--)
		for(int j=0;j<26;j++)
			if(s[i+1]==j+'a')
				nxt[i][j]=i+1;
			else
				nxt[i][j]=nxt[i+1][j];
	while(q--)
	{
		scanf("%s%d",o,&i);
		if(o[0]=='+')
		{
			scanf("%s",tmp);
			a[i]+=tmp[0];
			int h=tmp[0]-'a';
			if(i==1)
			{
				int t=a[1].size()-1;
				for(int j=0;j<a[2].size();j++)
					for(int k=0;k<a[3].size();k++)
					{
						dp[t][j][k]=nxt[dp[t-1][j][k]][h];
						if(j)
							dp[t][j][k]=min(dp[t][j][k],nxt[dp[t][j-1][k]][a[2][j]-'a']);
						if(k)
							dp[t][j][k]=min(dp[t][j][k],nxt[dp[t][j][k-1]][a[3][k]-'a']);
					}
			}
			else if(i==2)
			{
				int t=a[2].size()-1;
				for(int j=0;j<a[1].size();j++)
					for(int k=0;k<a[3].size();k++)
					{
						dp[j][t][k]=nxt[dp[j][t-1][k]][h];
						if(j)
							dp[j][t][k]=min(dp[j][t][k],nxt[dp[j-1][t][k]][a[1][j]-'a']);
						if(k)
							dp[j][t][k]=min(dp[j][t][k],nxt[dp[j][t][k-1]][a[3][k]-'a']);
					}
			}
			else
			{
				int t=a[3].size()-1;
				for(int j=0;j<a[1].size();j++)
					for(int k=0;k<a[2].size();k++)
					{
						dp[j][k][t]=nxt[dp[j][k][t-1]][h];
						if(j)
							dp[j][k][t]=min(dp[j][k][t],nxt[dp[j-1][k][t]][a[1][j]-'a']);
						if(k)
							dp[j][k][t]=min(dp[j][k][t],nxt[dp[j][k-1][t]][a[2][k]-'a']);
					}
			}
		}
		else
			a[i].pop_back();
		puts(dp[a[1].size()-1][a[2].size()-1][a[3].size()-1]<=n?"YES":"NO");
	}
	return 0;
} 
### 关于Codeforces平台上的动态规划问题 在Codeforces这样的编程竞赛平台上,动态规划(Dynamic Programming, DP)是一类非常重要的算法技术。这类题目通常涉及优化子结构和重叠子问题两个特性。 #### 动态规划示例解析 考虑一个典型的DP问题,在给定条件下求解最优方案的数量或具体路径等问题。例如,在某些情况下,可能需要计算达到特定状态所需的最少步数或是最大收益等[^1]。 对于具体的例子而言,假设有一个序列`a[]`,目标是从左到右遍历此序列并决定是否选取当前元素加入集合中,最终目的是让所选元素之和尽可能大而不超过某个上限值M。这个问题可以通过定义二维数组dp[i][j]表示从前i个物品里挑选若干件放入容量为j的背包可以获得的最大价值来建模: - 如果不取第i项,则`dp[i][j]=dp[i&minus;1][j]`; - 若选择第i项且其重量w不超过剩余空间j,则更新为`max(dp[i&minus;1][j], dp[i&minus;1][j-w]+v)`其中v代表该项的价值; 最后的结果保存在`dp[n][m]`处(n为总项目数量,m为目标体积)[^2]。 ```cpp #include <iostream> using namespace std; const int N = 1e3 + 5; int w[N]; // weights of items int v[N]; // values of items long long f[N][N]; void knapsack(int n, int m){ for (int i = 1; i <= n; ++i) { for (int j = 0; j <= m; ++j) { f[i][j] = f[i - 1][j]; if(j >= w[i]) f[i][j] = max(f[i][j],f[i - 1][j - w[i]] + v[i]); } } } ``` 上述代码展示了如何利用记忆化搜索的方式实现简单的0/1背包问题解决方案,这同样适用于其他形式更复杂的动态规划挑战。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值