HDU 6078 Wavel Sequence【dp递推】【好题】【思维题】

Wavel Sequence

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 362    Accepted Submission(s): 175


Problem Description
Have you ever seen the wave? It's a wonderful view of nature. Little Q is attracted to such wonderful thing, he even likes everything that looks like wave. Formally, he defines a sequence a1,a2,...,an as ''wavel'' if and only if a1<a2>a3<a4>a5<a6...



Picture from Wikimedia Commons


Now given two sequences a1,a2,...,an and b1,b2,...,bm, Little Q wants to find two sequences f1,f2,...,fk(1fin,fi<fi+1) and g1,g2,...,gk(1gim,gi<gi+1), where afi=bgi always holds and sequence af1,af2,...,afk is ''wavel''.

Moreover, Little Q is wondering how many such two sequences f and g he can find. Please write a program to help him figure out the answer.
 

Input
The first line of the input contains an integer T(1T15), denoting the number of test cases.

In each test case, there are 2 integers n,m(1n,m2000) in the first line, denoting the length of a and b.

In the next line, there are n integers a1,a2,...,an(1ai2000), denoting the sequence a.

Then in the next line, there are m integers b1,b2,...,bm(1bi2000), denoting the sequence b.
 

Output
For each test case, print a single line containing an integer, denoting the answer. Since the answer may be very large, please print the answer modulo 998244353.
 

Sample Input
1 3 5 1 5 3 4 1 1 5 3
 

Sample Output
10
Hint
(1)f=(1),g=(2). (2)f=(1),g=(3). (3)f=(2),g=(4). (4)f=(3),g=(5). (5)f=(1,2),g=(2,4). (6)f=(1,2),g=(3,4). (7)f=(1,3),g=(2,5). (8)f=(1,3),g=(3,5). (9)f=(1,2,3),g=(2,4,5). (10)f=(1,2,3),g=(3,4,5).
 

Source

题意:给出一个有n(<=2000)个数字的序列 a(ai <=2000) 再给出一个有m(m<=2000)个数字的序列 b (bi<=2000) ,定义波浪序列为:x1<x2>x3<x4……(注意第一次必须是上升,不能是下降,也就是说第一项必须是波谷)。现在要求找到一个严格单调递增的序列 f:f1,f2,……fk。以及相对应的严格单调递增的序列g:g1,g2,……gk。(k>=1)使得每个a_fi = b_gi,同时满足a_f1,a_f2,a_f3……a_fk为波浪序列。求不同的fg映射有多少种选取方式。

a,b中分别从前向后选取k个数字。然后相对应的 a 中选择的每个位置的数字要和 b 中选择的对应位次的数字相同。(当然如果a数组出现过x,而b没有出现过x,显然x不可能被选取),而 f 、g 则是相对应的下标。要满足选取出来的这个数字序列是一个波浪序列。显然波浪序列中的数字分成两种:波峰和波谷。

总体来说,这个题就是a、b数组之间的匹配问题,同时满足是一个波浪序列。


则:

dp[i][j][0]表示以a[i]和b[j]为公共序列结尾且为波谷的情况总和。 
dp[i][j][1]则表示波峰的情况总和。 

sum[i][j][0]表示sum(dp[k][j][0] | 1<=k<=i-1) 
sum[i][j][1]则表示sum(dp[k][j][1] | 1<=k<=i-1)    (相当于固定b[j]这个数字,然后将所有可能性加起来)


那么这个过程中只要不断判断这个数字其前面可能的情况(若其为波峰则判断其前可满足作为波谷的数量cnt0,反之亦然),然后判断这个数字能不能加进这个波浪中不断记录结果即可。


注意因为第一次必须为上升,则表示第一个数字前为波峰所以cnt1=1,cnt0=0


这个题有两点值得借鉴:

1、动态规划递推过程中用波峰波谷作为状态转移的关键,然后进行递推

2、用sum来优化其前相加的结果,减少重复的相加运算。


以下是样例的递推过程,蓝色粗线代表程序执行过程,旁边的线代表当前所记录波浪的状态:




#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <cstring>
#define INF 0x3f3f3f3f
#define ms(x,y) memset(x,y,sizeof(x))
using namespace std;

typedef long long ll;
typedef pair<int, int> P;

const int maxn = 2e3 + 10;
const int mod = 998244353;

int n, m;
int a[maxn], b[maxn];
ll dp[maxn][2];		//原为dp[i][j][2] 表示a数组枚举到第i个,b数组枚举到第j个,dp[i][j][0]表示当前选的数为波谷,反之同理,因为只用到上一个i-1所以利用滚动数组将其省去
ll sum[maxn][2];	//原为sum[i][j][2]表示a数组枚举从1到第i个,b数组枚举到第j个波峰(sum[i][j][1])或波谷(sum[i][j][0])的和

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		ll ans = 0;
		scanf("%d%d", &n, &m);
		ms(sum, 0);
		ms(dp, 0);
		for (int i = 1; i <= n; i++)
			scanf("%d", a + i);
		for (int i = 1; i <= m; i++)
			scanf("%d", b + i);
		for (int i = 1; i <= n; i++)
		{
			ll cnt1 = 1;	//最后一个是波峰的数量
			ll cnt0 = 0;	//最后一个是波谷的数量
			for (int j = 1; j <= m; j++)
			{
				dp[j][0] = dp[j][1] = 0;
				if (a[i] == b[j])	//【判断能否加进来】当相同时可以将当前的数作为 波峰(当前数做波峰则看其前面可作为他的波谷的数量cnt0)或波谷(其前cnt1种可能)时,前面所有可能的情况加到ans中
				{
					dp[j][0] = cnt1;
					dp[j][1] = cnt0;
					ans = (ans + cnt1 + cnt0) % mod;
				}
				else if (b[j] < a[i]) cnt0 = (cnt0 + sum[j][0]) % mod;	//说明当a[i]被选中时,前面的以b[j]结尾的可以作为波谷(所以将其记录到cnt0,又因为前面的作为波谷,所以为sum[j][0])
				else cnt1 = (cnt1 + sum[j][1]) % mod;	//反之亦然
			}
			for (int j = 1; j <= m; j++)
			{
				if (b[j] == a[i])
				{
					sum[j][0] = (sum[j][0] + dp[j][0]) % mod;	//以数字b[j]作为谷底的所有可能加进去,方便下次统计,减少重复计算(dp的递推优化)
					sum[j][1] = (sum[j][1] + dp[j][1]) % mod;	//同理
				}
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}



可借鉴:点击打开链接的思路


posted @ 2017-08-04 23:21  Archger  阅读(238)  评论(0编辑  收藏  举报