CodeForces - 655E dp+贪心

题目链接:cf崩了,so。

参考博客:https://blog.csdn.net/kyleyoung_ymj/article/details/51628238

补题。

题意:

每个样例输入两行,第一行输入两个数字n,k,第二行输入一个字符串,你可以在第二行输入的字符串后面增加n个字符(k代表字符范围,比如k=2,那么你选择的字符有a,b;如果k=4,那么你可以选择的字符有a,b,c,d,就是从字符a开始的k个字符是可选择的)。你要使输入字符串在后面增加n个指定范围的字符之后得到的字符串的子序列数目最多,输出最大的子序列数量。记得还要包括一个空串。

子序列是可以不连续的。

仅供参考,错了请指正。

这里我们假设用一个二维数组dp[i][j]表示到位置i(第i个字符)为止,以字符 j+‘a‘ 结尾的的子序列数量。

那么假设现在我们要在第i个位置上放一个字符 j ,那么我们应该更新dp[i][j]的值。

假设sum表示的是所有的dp[i-1][j](0<=j<k)的和,那么dp[i][j]=(sum+1)%mod;

这时的dp[i][j]可以分解为dp[i-1][j]+(sum-dp[i-1][j])+1;

那么在位置i增加字符 j之后,就导致以字符 j结尾的子序列增加了(sum-dp[i-1][j])+1个了。

其实就是所有不以 j 字符结尾的子序列数量加1,那为什么是增加这么多呢??

我们来一个例子,现在在字符串aabac后面增加一个字符a,在加字符a之前,我们有以下子序列

以a结尾的:a,aa,ba,aaa,aba,aaba;

以b结尾的:b,ab,aab;

以c结尾的:c,ac,bc,aac,abc,bac,aaac,aabc,abac,aabac;

好,现在我们要在后面加一个字符 ‘a‘ 了,在这之前,我们不妨给上面的所有子序列再按照另一种分类来排列一遍(加了一个空串):

1.空串,a,aa,aaa
2.b,ba
3.ab,aba
4.aab,aaba
5.c
6.ac
7.bc
8.aac
9.abc
10.bac
11.aaac
12.aabc
13.abac
14.aabac

这里的每一行的前面的字符串在增加字符‘a‘之后就变成了后面的字符串,也就是说前面的字符串是后面字符串的前缀。

也就是说除了每一行的最后一个字符串(子序列)------>>它增加字符a之后会导致以a结尾的子序列增加1,前面的所有的子序列加上字符a之后得到的子序列实际上都是已经出现过的,即已经计算在dp[i-1][j]中了,而不是因为位置 i 上增加字符a之后带来的。上面的每一行都只会增加一个新的子序列。现在我们再来看上面加粗的那四行,这四行的第一个子序列要么不是以字符a结尾,要么就是空串(第一行有"空串"两个字......),那么我们是不是可以用第一个子序列来替代整个一行子序列,也可以说是用每行的第一个子序列来代替这一行的最后一个子序列,这样我们就去掉了所有以a结尾的子序列带来的影响,而将它转化为了所有不以字符a结尾的子序列带来的影响,当然,还包括一个空串。上面有14行,在字符串aabac后面增加一个字符a之后,增加的子序列就是14个。

所以dp[i][j]=dp[i-1][j]+(sum-dp[i-1][j])+1。

计算完dp[i][j]之后,这个时候sum实际上应该更新了,因为dp[i-1][j]变成了dp[i][j]

此时 sum=(sum-dp[i-1][j]+dp[i][j])=sum-dp[i-1][j]+(sum+1)=2*sum-dp[i-1][j]+1;

在我们增加字符 j 的时候,为了使sum的值最大,我们肯定是要挑一个dp[i-1][j]的值最小的啦,这个好像叫贪心...

最后,对于已经给出的字符串,我们直接用上面的公式计算就可以了,而对于字符串后面要增加的n个字符,我们每次都找到一个字符 j ,它的dp[i-1][j]的值最小,这样根据上面的公式就可以使sum一直保持最大了,然后因为要取模,所以我们不可以直接比较大小,而是应该记录字符 ‘a‘ 到字符 ‘a ‘+k每一个字符最后出现的位置,找出现位置最小的那个字符加到字符串最后,因为位置最小的肯定是值最小的。

代码(变成一维的了):

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque>
using namespace std;
typedef long long ll;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 2000005
const int mod=1e9+7;
ll n,m,k,t;
char str[maxn];
ll dp[26];//存以每一个字符结尾的子序列的数量
int last[26];//存每一个字符最后出现的位置
int main()
{
    scanf("%lld%lld",&n,&k);
    scanf("%s",str+1);
    memset(last,0,sizeof(last));
    int len=strlen(str+1);
    ll sum=0;
    for(int i=1;i<=len;i++){//处理输入的字符串
        int id=str[i]-‘a‘;
        last[id]=i;//更新最后出现的位置
        ll pre=dp[id];//把位置在i-1时的dp[id]暂时存一下
        dp[id]=(sum+1+mod)%mod;//更新dp[id]
        sum=(sum-pre+dp[id]+mod)%mod;//更新sum
    }
    for(int i=1;i<=n;i++){//增加n个给定范围的字符
        int Min=INF;// 找最小的位置
        int id;//存位置最小的字符
        for(int j=0;j<k;j++){
            if(last[j]<Min){
                Min=last[j];
                id=j;
            }
        }
        last[id]=i+len;//更新最后出现大的位置
        ll pre=dp[id];//把位置在i-1时的dp[id]暂时存一下
        dp[id]=(sum+1+mod)%mod;//更新dp[id]
        sum=(sum-pre+dp[id]+mod*2)%mod;//更新sum
    }
    printf("%lld\n",sum+1);//最后结果加1,因为还有一个空串
    return 0;
}

原文地址:https://www.cnblogs.com/6262369sss/p/11986388.html

时间: 2024-10-31 13:40:12

CodeForces - 655E dp+贪心的相关文章

Codeforces 459E Pashmak and Graph(dp+贪心)

题目链接:Codeforces 459E Pashmak and Graph 题目大意:给定一张有向图,每条边有它的权值,要求选定一条路线,保证所经过的边权值严格递增,输出最长路径. 解题思路:将边按照权值排序,每次将相同权值的边同时加入,维护每个点作为终止点的最大长度即可. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 3

Codeforces 77C 树形dp + 贪心

题目链接:点击打开链接 题意: 给定n个点, 每个点的豆子数量 下面是一棵树 再给出起点 每走到一个点,就会把那个点的豆子吃掉一颗. 问:回到起点最多能吃掉多少颗豆子 思路:树形dp 对于当前节点u,先把子节点v都走一次. 然后再往返于(u,v) 之间,直到u点没有豆子或者v点没有豆子. dp[u] 表示u点的最大值.a[u] 是u点剩下的豆子数. #include <cstdio> #include <vector> #include <algorithm> #inc

Codeforces 830A. Office Keys (背包dp+贪心) / (二分+贪心)

题目链接: http://codeforces.com/problemset/problem/830/A 题意: n个人,k个钥匙(n<=k),p表示这些人要到达的位置 给出n个人的位置以及钥匙的位置,问花时间最多的那个人用时最少是多少?? 思路: 二分+贪心: 二分最少时间,需要对a,b位置数组排序,我们check函数只需要从左到右一个一个找过去,因为如果选后边的点,可能会使结果更差,假如当前这个人选后面的点,那可能会选中后面的人可以选的唯一的钥匙,不会使解更优. check(40)的时候答案

codeforces219C - Color Stripe DP+贪心

题意:给你n,k,大意就是说给你一个已经涂满颜色长为n的字符串,现有k种颜色可以选择,问你最少要改变多少个箱子的颜色使得相邻箱子之间颜色不同. 解题思路:当k = 2 时单独讨论,不能用贪心,其余情况都可贪心得到. 解题代码: 1 // File Name: 219c.cpp 2 // Author: darkdream 3 // Created Time: 2014年07月26日 星期六 15时45分56秒 4 5 #include<vector> 6 #include<list>

ZOJ 2109 FatMouse&#39; Trade (背包 dp + 贪心)

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1109 FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the warehouse containing his favorite food, JavaBean. The warehouse has N rooms. The i-th room contains J

UVALive 4731 dp+贪心

这个题首先要利用题目的特性,先贪心,否则无法进行DP 因为求期望的话,越后面的乘的越大,所以为了得到最小值,应该把概率值降序排序,把大的数跟小的系数相乘 然后这种dp的特性就是转移的时候,由 i推到i+1每次添加一个数,就要考虑这个新数应该和谁放在一组,枚举他放在哪一组即可 dp[i][j]代表当前第i个数有j个分组时候的最小值 dp[i][j]=dp[k][j-1]+i(prefix[i]-prefix[k-1]),k代表枚举第几个数开始和当前新添加的数为一组,prefix为前缀和,为了迅速得

HDU 5303(Delicious Apples- 环上折半dp+贪心)

Delicious Apples Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others) Total Submission(s): 1585    Accepted Submission(s): 514 Problem Description There are n apple trees planted along a cyclic road, which is L metres

uva 1534 - Taekwondo(dp+贪心)

题目连接:uva 1534 - Taekwondo 题目大意:有两组什么东西,题目背景有点忘记了,就是给出两组数,两组个数分别为n,m,要求找出min(n,m)对数,每个数最多最多选一次,使得这min(n,m)对数ai,bi,ai-bi的绝对值之和最小. 解题思路:贪心,将两组数分别排序,然后dp[i][j]表示i对,匹配到j时候的最优解. #include <cstdio> #include <cstring> #include <cmath> #include &l

Codeforces 413C Jeopardy!(贪心)

题目链接:Codeforces 413C Jeopardy! 题目大意:给出n个关卡,每个关卡闯关成功会得到相应的分数,有m个关卡闯关成功之后,可以选择不加上该关卡的分,而是将已有的分数翻倍,现在有一位选手已经有能力闯过所有的关卡,问说他能得到的最大分数是多少. 解题思路:贪心,将可以翻倍的关卡放在后面比,不能翻倍的关卡放在前面比,然后在按照关卡分数大的先比,如果该关卡分数可以翻倍,就判断是当前关卡的分数高还是已有的分数高. #include <cstdio> #include <cst