hdu4455 dp

这个题目是2012年的区域赛的题目,是一个典型的dp题目,但是状态定义,即状态转移方程真是不太好想。自己一直在往多维的状态上想,但是都觉得不合理。后来,我也是看了大神的题解才明白的。自己就试着写下自己对这个题目的理解。题目的意思比较好懂:就是给定一个长度为n的序列。然后让你求出它的所有长度为m的连续子序列中,每个序列里不同元素的个数,并将求其和。

题目思路:状态定义为dp[i]表示子序列的长度为i时各个子序列中不同元素个数的和。状态转移方程比较难想,也不太好理解好实现,具体看代码注释吧。

#include
#include
#include
#define MAX 1000010
#define ll __int64

using namespace std;

ll dp[MAX];       //dp[i]表示子序列长度为i时的结果为多少
int a[MAX],f[MAX],c[MAX],s[MAX];

//f[i]记录a[i]最近一次出现的位置
//c[i]记录子序列的长度为i时,整个序列有多少对相同的元素 
//s[i]表示最后i个元素中有多少不同的元素 

int main(){
	int n,q;
	while (scanf("%d",&n)&& n != 0)	{
		memset(s,0,sizeof(s));
		memset(f,0,sizeof(f));
		memset(c,0,sizeof(c));
		for(int i = 1; i<=n; i++) {    //输入数据并求c[]数组,注意初始化的时候将每个元素第一次出现的位置定为0,这样做有助于后面的计算。
			scanf("%d",&a[i]);
			c[i-f[a[i]]]++;
			f[a[i]] = i;
		}
		
		memset(f,0,sizeof(f));
		
		f[a[n]] = 1;              //求s[]数组
		s[1] = 1;
		for (int i = 2; i<=n; i++){
			if (f[a[n-i+1]] == 0){
				f[a[n-i+1]] = 1;
				s[i] = s[i-1] + 1;
			}
			else{
				s[i] = s[i-1];
			}
		}
		 
		dp[1] = n;
		int tmp = n;             //注意tmp的值得更新方法,根据是:长度为i的子序列中相同元素的对数 = 长度小于i的子序列中相同元素的对数加上子序列长度为i时相同元素的对数。 
		for (int i = 2; i<=n; i++){
			dp[i] = dp[i-1] - s[i-1];   //从长度为i-1变到长都为i为“失去”最后i-1个元素
			tmp -= c[i-1];
			dp[i] += tmp;
		}
		
		scanf("%d",&q);
		int t;
		while (q--){
			scanf("%d",&t);
			printf("%I64d\n",dp[t]);
		}
	}
	return 0;
}



你可能感兴趣的:(hdu4455 dp)