cf1900左右的dp

博客内容涉及多个Codeforces(CF)比赛中的难题,主要探讨动态规划(dp)的运用,包括状态拆分、子序列匹配、矩阵快速幂优化等技术。通过分析题目如CF296B、CF362C、CF229D、CF223B、CF82D、CF847E、CF222E和CF417D,解释如何构建并优化dp递推式,解决DNA序列计算和子序列计数问题。还提到了贪心策略在某些问题中的结合使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


CF296B 状态拆分

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ll long long
#define pii pair<int,int>
#define re register
const int maxn = 1e5+10;
const int mx = 40;
const ll mod = 1e9+7;
const ll inf = 34359738370;
const int INF = 1e9+7;
const double pi = acos(-1.0);
//给定两个数字字符串 (含? 可以表示任意一个数字 )
//定义字符串s w不匹配为 : 存在 i,j s[i]>w[i] s[j]<w[j] 
//问不匹配的情况一共有多少种 对1e9+7取模
char s[maxn],w[maxn];
ll dp[maxn][2][2];
//定义子问题:前i个数 s[j]>w[j] 是否存在 s[j]<w[j]是否存在 一共4种状态  可递推

//dp[i][1/0][1/0]表示完成了前i位 (不)存在s[j]>w[j] 且(或) s[j] < w[j]的情况
//边界 dp[0][0][0]=1 dp[0][0][1]=dp[0][1][0]=dp[0][1][1]=0

//转移过程就根据当且字符判断出当前状态可以由前一位的哪几个状态转移得到
int n;
int main()
{
   
    scanf("%d %s %s",&n,s+1,w+1);
    dp[0][0][0]=1;
    for(int i=1;i<=n;i++) 
    {
   
        if(s[i]=='?' && w[i]=='?') 
        {
   
            dp[i][0][0] = dp[i-1][0][0]*10 % mod;
            dp[i][0][1] = (dp[i-1][0][0]*45 % mod + dp[i-1][0][1]*55 % mod) % mod ; 
            dp[i][1][0] = (dp[i-1][0][0]*45 % mod + dp[i-1][1][0]*55 % mod) % mod ; 
            dp[i][1][1] = (dp[i-1][0][1]*45 % mod + dp[i-1][1][0]*45 % mod + dp[i-1][1][1]*100 % mod) % mod ;
        }
        else if(s[i]=='?' && w[i] != '?') 
        {
   
            int d=w[i]-'0';
            dp[i][0][0] = dp[i-1][0][0];
            dp[i][0][1] = (dp[i-1][0][0]*d % mod + dp[i-1][0][1]*(d+1) % mod) % mod ; 
            dp[i][1][0] = (dp[i-1][0][0]*(9-d) % mod + dp[i-1][1][0]*(9-d+1) % mod) % mod ; 
            dp[i][1][1] = (dp[i-1][0][1]*(9-d) % mod + dp[i-1][1][0]*d % mod + dp[i-1][1][1]*10 % mod) % mod ;
        }
        else if(s[i]!='?' && w[i]=='?') 
        {
   
            int d=s[i]-'0';
            d=9-d;
            dp[i][0][0] = dp[i-1][0][0];
            dp[i][0][1] = (dp[i-1][0][0]*d % mod + dp[i-1][0][1]*(d+1) % mod) % mod ; 
            dp[i][1][0] = (dp[i-1][0][0]*(9-d) % mod + dp[i-1][1][0]*(9-d+1) % mod) % mod ; 
            dp[i][1][1] = (dp[i-1][0][1]*(9-d) % mod + dp[i-1][1][0]*d % mod + dp[i-1][1][1]*10 % mod) % mod ;
        }
        else 
        {
   
            int ds=s[i]-'0',dw=w[i]-'0';
            if(ds==dw)
            {
   
                dp[i][0][0]=dp[i-1][0][0];
                dp[i][1][1]=dp[i-1][1][1];
                dp[i][1][0]=dp[i-1][1][0];
                dp[i][0][1]=dp[i-1][0][1];
            }
            else if(ds > dw) 
            {
   
                dp[i][0][0]=0;
                dp[i][1][1]=(dp[i-1][1][1]+dp[i-1][0][1]) % mod;
                dp[i][0][1]=0;
                dp[i][1][0]=(dp[i-1][0][0]+dp[i-1][1][0]) % mod;
            }
            else 
            {
   
                dp[i][0][0]=0;
                dp[i][1][0]=0;
                dp[i][1][1]=(dp[i-1][1][1]+dp[i-1][1][0]) % mod;
                dp[i][0][1]=(dp[i-1][0][0]+dp[i-1][0][1]) % mod;
            }
        }
    }
    cout<<dp[n][1][1]<<'\n';
    return 0;
}

CF362C

#include<bits/stdc++.h>
using namespace std;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值