两道很好的dp题目【4.29考试】

A

  问题描述:

  对于一个排列,考虑相邻的两个元素,如果后面一个比前面一个大,表示这个位置是上升的,用I表示,反之这个位置是下降的,用D表示。如排列3,1,2,7,4,6,5可以表示为DIIDID。

  现在给出一个长度为n-1的排列表示,问有多少种1到n的排列满足这种表示。

  输入:

  一个字符串S,S由I,D,?组成.?表示这个位置既可以为I,又可以为D。

  输出:

  有多少种排列满足上述字符串。输出排列数模1000000007

  样例输入:

  ?D

  样例输出:

  3

  数据范围:

  20%的数据 S长度<=10

  100%的数据 S长度<=1000

  分析:怎么做?先想一想,i+1个数的方法数可以用i个数的方法数推出来,自然就想到了dp。一看数据范围,1000,只可以开二维,多半是二维dp。

  dp[i][j] i表示共有i个数,j表示当前序列的最后一位为j。可以发现一个东西:前面i个数范围都在1~i且1~i中的每个数都只出现了1次。得出下列结论:

  1.‘D‘ 呈下降趋势,当前这位填j,前面那位可以填j+1~i-1(i表示序列长度,已经+1),但这样的话,前面又有一个j,和当前这位j矛盾。我们可以视作前面所有>=j的数全部+1,来避免这种矛盾,所以当前位为j的前一位实际可以填j~i-1。(这一句重点)

  2.‘I‘ 呈上升趋势,当前这位填j,前一位填1~j-1的数。为什么不能填j?因为当前位填j后实际上整个序列是少一个为i(i表示序列长度,已经+1)的数的,我们只能把每个>=j的数视作+1来避免这种bug。如果当前位填j,前一位填j,前面那位会被视作+1,矛盾。

  3.‘?‘ D和I的情况加起来,耶。

  解决。

CODE:

 1 #include<cstdio>
 2 int f[1005][1005],mod=1000000007;
 3 int main()
 4 {
 5     freopen("B.in","r",stdin);
 6     freopen("B.out","w",stdout);
 7     char c;int i=1,ans=0;
 8     c=getchar();f[1][1]=1;
 9     while(c==‘D‘||c==‘I‘||c==‘?‘)
10     {
11         i++;
12         for(int j=1;j<i;j++)
13         f[i-1][j]=(f[i-1][j]+f[i-1][j-1])%mod;
14         if(c==‘I‘){
15             for(int j=2;j<=i;j++)
16             f[i][j]=f[i-1][j-1];
17         }
18         if(c==‘D‘){
19             for(int j=1;j<i;j++)
20             f[i][j]=(f[i-1][i-1]-f[i-1][j-1]+mod)%mod;
21         }
22         if(c==‘?‘){
23             for(int j=1;j<i;j++)
24             f[i][j]=(f[i-1][i-1]-f[i-1][j-1]+mod)%mod;
25             for(int j=2;j<=i;j++)
26             f[i][j]=(f[i-1][j-1]+f[i][j])%mod;
27         }
28         c=getchar();
29     }
30     for(int j=1;j<=i;j++)
31     ans=(ans+f[i][j])%mod;
32     printf("%d",ans);return 0;
33 }

B

  问题描述:

  小A非常喜欢字符串,所以小K送给了小A两个字符串作为礼物。两个字符串分别为X,Y。小A非常开心,但在开心之余她还想考考小K。小A定义L为X与Y的最长公共子序列的长度(子序列在字符串内不一定连续,一个长度为L的字符串有2^L个子序列,包括空子序列)。现在小A取出了X的所有长度为L的子序列,并要求小K回答在这些子序列中,有多少个是Y的子序列。因为答案可能很大,所以小K只需要回答最终答案模10^9 + 7。

  输入:

  第一行包含一个非空字符串X。

  第二行包含一个非空字符串Y。

  字符串由小写英文字母构成。

  输出:

  对于每组测试数据输出一个整数,表示对应的答案。

  样例输入:

  aa

  ab

  样例输出:

  2

  数据范围:

  对于20%的数据,1 <= |X|,|Y| <= 10

  对于100%的数据,1 <= |X|,|Y| <= 1000

  

  

  分析:一道很有趣的题,我没看出是dp

  题目必须读懂,虽说是求a中所有长度为L的序列在b中出现的个数,其实就是求最大长度的lcs有多少种方法。。。cnm坑比题。知道这一点之后,dp方程也就不难想了。

  由于范围是1000,所以只能开二维。dp[i][j]定义a中的前i个字符和b中的前j个字符构成的长度为 lcs(i,j)的公共子序列有多少种。

  肯定先求lcs,每个位置的lcs都要求。然后dp怎么转移呢?由于是在a中取序列,只考虑a中的某位置的字符取还是不取。

  1.如果不取当前这第i位的字符,且lcs[i][j]=lcs[i-1][j],说明啥?说明了a中i位置这个字符对答案并无贡献,所以只用继承就可以。dp[i][j]+=dp[i-1][j]

  2.取第i位字符。如果取了这个字符后,会有一个新的lcs长度刚好达到lcs[i][j],那么这个位置的方法数也是可以加过来的。那么我们怎么表示新lcs的结尾在b中对应的是哪个字符(设为x,其最后位置为p)?预处理一下每个字符在b中出现的最后位置。为什么非要p,万一前面的x也可以构成新lcs呢?贪心,后面方法数的肯定比前面的方法数多,取p方法数一定最优。dp[i][j]+=dp[i-1][p-1]

  PS:为什么这样做得到的一定是长度为lcs(a,b)的公共子序列,很有意思,lcs(a,b)中存的一定是最长lcs的长度,如果,只有长度满足才会递推过去,啊哈哈哈,啊哈哈哈。

  完美解决,耶!

CODE:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define ll long long
 7 #define inf 2147483647
 8 #define N 1005
 9 #define mod 1000000007
10 using namespace std;
11 int lcs[N][N],dp[N][N],ls[N][27];
12 char a[N],b[N];
13 void lalala(){
14     int la=strlen(a+1),lb=strlen(b+1);
15     for(int i=1;i<=la;i++)
16     for(int j=1;j<=lb;j++){
17         if(a[i]==b[j])lcs[i][j]=lcs[i-1][j-1]+1;
18         else lcs[i][j]=max(lcs[i][j-1],lcs[i-1][j]);
19     }
20 }
21
22 void nanana(){
23     int lb=strlen(b+1);
24     for(int i=1;i<=lb;i++){
25         memcpy(ls[i],ls[i-1],sizeof(ls[i-1]));
26         ls[i][b[i]-‘a‘+1]=i;
27     }
28 }
29
30 int aaa(){
31     int la=strlen(a+1),lb=strlen(b+1);
32     for(int i=0;i<=la;i++)dp[i][0]=1;
33     for(int j=0;j<=lb;j++)dp[0][j]=1;
34     for(int i=1;i<=la;i++)
35     for(int j=1;j<=lb;j++){
36         if(lcs[i-1][j]==lcs[i][j]){
37             dp[i][j]+=dp[i-1][j];
38             dp[i][j]%=mod;
39         }
40         int p=ls[j][a[i]-‘a‘+1];
41         if(lcs[i][j]==lcs[i-1][p-1]+1&&p){
42             dp[i][j]+=dp[i-1][p-1];
43             dp[i][j]%=mod;
44         }
45     }
46     return dp[la][lb];
47 }
48
49 int main(){
50     freopen("C.in","r",stdin);
51     freopen("C.out","w",stdout);
52     scanf("%s",a+1);
53     scanf("%s",b+1);
54     lalala();
55     nanana();
56     printf("%d",aaa());
57     return 0;
58 }

    

时间: 2024-10-12 20:14:22

两道很好的dp题目【4.29考试】的相关文章

两道关于逆序对的题目

题目一:合并有序数组:给定两个排序后的数组A和B,其中A的末端有足够的缓冲空间容纳B.编写一个方法,将B合并入A并排序. 思路:注意这儿已经说明A的空间足够,那就说明不能再开辟辅助空间.然后合并的话可以采用归并的思想,就能解决这个问题. 代码: import java.util.Arrays; public class 合并有序数组 { public static void main(String[] args) { int A[] = new int[12]; for (int i = 0;

xtu 1035 与xtu 1036 两道出栈入栈题目,蛮有趣的

Description John是个小列车站的站长,每次列车在这里重新编组时他就很烦恼.因为站上只有一个人字形的编组轨道(如图),所有的列车车厢都是从人字轨的右边依次进去,从左边出来.但有一些编组顺序John总编不出来,John怀疑有些编组顺序是不可能完成的,可John又找不出那些是顺序是可以编组出,那些不可以.请你写一个程序帮助John辨别哪些编组可以完成,哪些不能完成. 输入: 第一行是一个整数K,表示有多少个测试用例,以后每行一个测试用例,每行为n+1个整数,第一个整数为n 表示有多少节车

两道有意思的题目

碰到两道有意思的题目,记录一下. 题目一: 问,对于任意一个正整数,是否存在一个它的倍数全是由1和0组成? 例如: 1 * 1 = 1 2 * 5 = 10  (2的5倍是10,10由1和0组成) 3 * 37 = 111 (3 的 37 倍是111,111 全部由1组成) 4 * 25 = 100 (4 的 25 倍是100,100 由1和0组成) 5 * 20 = 100 (5 的 20 倍是100,100由1 和 0 组成) …… 现在需要判断,随便给一个正整数,是否存在一个它的倍数满足题

分享两道笔试题目

前几天,给成都的某家公司投了个简历,给发了两道笔试题目,与大家分享一下.附上自己的解题过程,写得不好的地方,还请博友多多指教. 一 .  设计程序输出销售及收费清单 一个电商平台对在其平台之上销售的除了书籍.食品以及药物以外的商品收取 10% 的费用.而对于进口的商品则额外收取 5% 的附加费用.对于平台抽取的费用计算时,舍入的规则是:对于 n% 抽取率,价格为 p的商品, np/100 的值就近舍入到 0.05(如: 7.125 -> 7.15, 6.66 -> 6.70 ). 卖家卖出一些

告诉我图样图森破的两道简单C++笔试题

今晚刷了一大堆的笔试题,中规中矩,但是有两道做得很快但是都错了的题目,印象深刻. (要找工作的大四渣有没有共鸣,在学校明明很努力,但是总是跟不上时代,没有厉害的项目,也没有过人的竞赛成绩,内推屡屡失败,前天阿里巴巴在线笔试也被虐死,真心迷惘,唯独刷题搞笔试了.) 第一道题是关于宏定义的. #include<iostream> using namespace std; #define fun(n) (n-1)*n int main() { int x=3; cout<<fun(x+3

两道拓扑排序的问题

多久没写东西了啊.... 两道拓扑排序Liv.1的题....方法是一样的~~ <拓扑排序·二> 题目:http://hihocoder.com/contest/hiho81/problem/1 一个电脑网路,单向边,如果存在边u->v,那么u的病毒会感染到v. 要点,不存在环!那么如果u的入度=0的话,那么u中的病毒数不会再变化. 想到拓扑排序.不断删去入度为0的点.每次删去节点u,如果存在u->v,那么病毒数 num[v] += num[u].问题解决. (用queue实现拓扑排

ACM/ICPC 之 SPFA范例两道(POJ3268-POJ3259)

两道以SPFA算法求解的最短路问题,比较水,第二题需要掌握如何判断负权值回路. POJ3268-Silver Cow Party //计算正逆最短路径之和的最大值 //Time:32Ms Memory:360K #include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<algorithm> using namespace std; #define

[sdut]2623+[sdut]2878//四五届省赛中的两道数学期望

两道数学期望的题今天一起总结上来. 1.the number of steps(第四届省赛) 1 #include <iostream> 2 #include <string.h> 3 #include <iomanip> 4 using namespace std; 5 double dp[100][100]; 6 int n; 7 double a,b,c,d,e; 8 9 int main() 10 { 11 while(cin>>n&&

几道经典的SQL笔试题目

几道经典的SQL笔试题目(有答案) (1)表名:购物信息 购物人      商品名称     数量 A            甲          2 B            乙          4 C            丙          1 A            丁          2 B            丙          5 …… (其他用户实验的记录大家可自行插入) 给出所有购入商品为两种或两种以上的购物人记录 答:select * from 购物信息 wher