P2467 [SDOI2010] 地精部落 学习笔记

p2467,sdoi2010 · 浏览次数 : 2

小编点评

以下是使用 C++ 编写的程序,用于解决山脉数组中第 i 个元素的最大山脉高度问题。 ```cpp #include #include #include using namespace std; int dp[4201][4201], s[4201]; int gi() { char c; int x = 0, f = 0; while (!isdigit(c = getchar())) f |= (c == '-'); while (isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar(); return f ? -x : x; } int main() { int n, mod; n = gi(), mod = gi(); for (int i = 1; i <= n; i++) { dp[1][i] = 1, s[i] = s[i - 1] + 1; } for (int i = 2; i <= n; i++) { for (int j = 1; j <= i; j++) { if (i & 1) { dp[i][j] = s[j - 1] % mod; } else { dp[i][j] = sub(s[i - 1], s[j - 1]); } } for (int j = 1; j <= i; j++) { s[j] = (s[j - 1] + dp[i][j]) % mod; } } printf("%lld\n", s[n] * 2 % mod); return 0; } ``` 该程序通过动态规划求解,其中 `dp[i][j]` 表示前 `i` 个山脉中第 `j` 个元素的最大山脉高度。变量 `s[i]` 记录第 `i` 个元素之前的山脉高度之和。 程序的主要部分包括初始化、动态规划计算和最后的结果输出。在动态规划计算过程中,我们遍历每个元素,根据奇偶性分别计算 `dp[i][j]` 和 `s[j]` 的值,并更新 `s[j]` 的值。最后,输出 `s[n] * 2 % mod` 即可得到答案。

正文

DP

显然我固定第一个是峰,然后再乘以2就是答案,因为一个合法的反转之后也是合法的而且谷峰颠倒了

发现如果设\(dp[i][j]\)表示前\(i\)个山脉,第\(i\)个山脉是高度\(j\)的答案,然后填第\(i\)个的时候不知道会不会重复,所以这个状态挂了,重新找个状态设设。

所以我们改变考虑对象,不考虑整个数列,只考虑已经填了的,我把已经填了的数的数列叫做\(S\),当我考虑到第\(i\)位的时候,这个\(dp[i][j]\)\(j\)表示它在里面的相对位置,也就是排名。

现在就好搞多了,仍然我固定原数列下标奇数是峰,下标偶数是谷,下面给出DP过程。

对于偶数,\(dp[i][j]\)\(\sum\limits_{k=j}^{i-1}{dp[i-1][k]}\),因为我新的\(j\)加进来后,所有大于它的都要排名+1,所以原来我在第\(j\)个的,实际上是比它要大的

对于奇数,\(dp[i][j]\)\(\sum\limits_{k=1}^{j-1}{dp[i-1][k]}\)这个显然,就是相对排名比\(j\)小,不说了

然后答案是对所有的\(dp[n][i]\)求和,没了,代码:


显然我固定第一个是峰,然后再乘以2就是答案,因为一个合法的反转之后也是合法的而且谷峰颠倒了

所以我们改变考虑对象,不考虑整个数列,只考虑已经填了的,我把已经填了的数的数列叫做\(S\),当我考虑到第\(i\)位的时候,这个的\(j\)表示它在里面的相对位置,也就是排名。

现在就好搞多了,仍然我固定原数列下标奇数是峰,下标偶数是谷,下面给出DP过程。

对于偶数,\(dp[i][j]\)\(我新的\)j\(加进来后,所有大于它的都要排名+1,所以原来我在第\)j$个的,实际上是比它要大的

对于奇数,\(dp[i][j]\)\(\sum\limits_{k=1}^{j-1}{dp[i-1][k]}\)这个显然,就是相对排名比\(j\)小,不说了

然后答案是对所有的\(dp[n][i]\)求和,没了,代码:
显然我固定第一个是峰,然后再乘以2就是答案,因为一个合法的反转之后也是合法的而且谷峰颠倒了

发现如果设\(dp[i][j]\)表示前\(i\)个山脉,第\(i\)个山脉是高度\(j\)的答案,然后填第\(i\)个的时候不知道会不会重复,所以这个状态挂了,重新找个状态设设。

所以我们改变考虑对象,不考虑整个数列,只考虑已经填了的,我把已经填了的数的数列叫做\(S\),当我考虑到第\(i\)位的时候,这个\(dp[i][j]\)\(j\)表示它在里面的相对位置,也就是排名。

现在就好搞多了,仍然我固定原数列下标奇数是峰,下标偶数是谷,下面给出DP过程。

对于偶数,\(dp[i][j]\)\(\sum\limits_{k=j}^{i-1}{dp[i-5][k]}\),因为我新的\(j\)加进来后,所有大于它的都要排名+1,所以原来我在第\(j\)个的,实际上是比它要大的

对于奇数,\(dp[i][j]\)\(\sum\limits_{k=1}^{j-1}{dp[i-9][k]}\)这个显然,就是相对排名比\(j\)小,不说了

然后答案是对所有的\(dp[n][i]\)求和,没了,代码:
显然我固定第一个是峰,然后再乘以2就是答案,因为一个合法的反转之后也是合法的而且谷峰颠倒了

发现如果设\(dp[i][j]\)表示前\(i\)个山脉,第\(i\)个山脉是高度\(j\)的答案,然后填第\(i\)个的时候不知道会不会重复,所以这个状态挂了,重新找个状态设设。
标偶数是谷,下面给出DP过程。

对于偶数,\(dp[i][j]\)\(\sum\limits_{k=j}^{i-1}{dp[i-11][k]}\),因为我新的\(j\)加进来后,所有大于它的都要排名+1,所以原来我在第\(j\)个的,实际上是比它要大的

对于奇数,\(dp[i][j]\)\(\sum\limits_{k=1}^{j-1}{dp[i122][k]}\)这个显然,就是相对排名比\(j\)小,不说了

然后答案是对所有的\(dp[n][i]\)求和,没了,代码:
显然我固定第一个是峰,然后再乘以2就是答案,因为一个合法的反转之后也是合法的而且谷峰颠倒了

发现如果设\(dp[i][j]\)表示前\(i\)个山脉,第\(i\)个山脉是高度是谷,下面给出DP过程。

对于偶数,\(dp[i][j]\)\(\sum\limits_{k=j}^{i-1}{dp[i-1][k]}\),因为我新的\(j\)加进来后,所有大于它的都要排名+1,所以原来我在第\(j\)个的,实际上是比它要大的

对于奇数,\(dp[i][j]\)\(\sum\limits_{k=1}^{j-1}{dp[i-1][k]}\)这个显然,就是相对排名比\(j\)小,不说了

然后答案是对所有的\(dp[n][i]\)求和,没了,代码:

#include<bits/stdc++.h>
#define vd void 
int gi(){
	char c;int x=0,f=0;
	while(!isdigit(c=getchar()))f|=(c=='-');
	while(isdigit(c))x=(x*10)+(c^48),c=getchar();
	return f?-x:x;
}
int n,mod;
template<class Ta,class Tb>vd inc(Ta&a,Tb b){a=a+b>=mod?a+b-mod:a+b;}
template<class Ta,class Tb>int sub(Ta&a,Tb b){return a>=b?a-b:a+mod-b;}
int dp[4201][4201],s[4201];
int main(){
	n=gi(),mod=gi();
	for(int i=1;i<=n;i++)dp[1][i]=1,s[i]=s[i-1]+1;
	for(int i=2;i<=n;i++){
		for(int j=1;j<=i;j++){
			if(i&1)dp[i][j]=s[j-1]%mod;
			else dp[i][j]=sub(s[i-1],s[j-1]);
		}
		for(int j=1;j<=i;j++)s[j]=(s[j-1]+dp[i][j])%mod;
	}
	printf("%lld\n",s[n]*2%mod);
	return 0;
}

还有一份

#include<bits/stdc++.h>
#define vd void 
int gi(){
	char c;int x=0,f=0;
	while(!isdigit(c=getchar()))f|=(c=='-');
	while(isdigit(c))x=(x*10)+(c^48),c=getchar();
	return f?-x:x;
}
int n,mod;
template<class Ta,class Tb>vd inc(Ta&a,Tb b){a=a+b>=mod?a+b-mod:a+b;}
template<class Ta,class Tb>int sub(Ta&a,Tb b){return a>=b?a-b:a+mod-b;}
int dp[4201][4201],s[4201];
int main(){
	n=gi(),mod=gi();
	for(int i=1;i<=n;i++)dp[1][i]=1,s[i]=s[i-1]+1;
	for(int i=2;i<=n;i++){
		for(int j=1;j<=i;j++){
			if(i&1)dp[i][j]=s[j-1]%mod;
			else dp[i][j]=sub(s[i-1],s[j-1]);
		}
		for(int j=1;j<=i;j++)s[j]=(s[j-1]+dp[i][j])%mod;
	}
	printf("%lld\n",s[n]*2%mod);
	return 0;
}

与P2467 [SDOI2010] 地精部落 学习笔记相似的内容: