首页 > 其他分享 ><学习笔记> DP套DP

<学习笔记> DP套DP

时间:2024-02-06 15:12:39浏览次数:29  
标签:LCS int DP trans 转移 dp mathrm

游园会

考虑直接设 \(F(i,j)\) 表示对于兑奖串前 \(i\) 位匹配奖章串的 \(\mathrm{LCS}\) 位 \(j\) 的方案数,但是发现没有办法直接转移,因为对于匹配到奖章串不同位置新加一个字符情况是不一样的。

考虑 \(\mathrm{LCS}\) 的转移,设 \(dp(i,j)\) 表示兑奖串前 \(i\) 位与奖章串前 \(j\) 位的 \(\mathrm{LCS}\) 的长度,那么有转移

\[dp(i,j)=max\{ dp(i-1,j),dp(i,j-1),dp(i-1,j-1)+[A_i=b_j]\} \]

发现转移只跟 \(dp(i-1)\) 的状态相关,因为位数很小,所以我们考虑讲某个状态与奖章串的前 \(i\) 位的 \(\mathrm{LCS}\) ,设为 \(f_i\) 发现 \(\mathrm{LCS}\) 单增且增量为 \(1\),那么直接可以压成 \(\mathrm{01}\) 串。

那么内层 \(\mathrm{dp}\) 是一个匹配状态加一个字符会转移到的状态(求出值是什么),那么外层 \(\mathrm{dp}\) 是一个计数。

设 \(F(i,s)\) 表示到兑奖串前 \(i\) 位匹配状态为 \(s\) 的方案数,我们可以先预处理出来转移的指针 \(trans(s,c)\),此过程与 \(\mathrm{LCS}\) 的转移类似,然后就有转移,可以滚动

\[F(i,trans(s,c)) \leftarrow F(i-1,s) \]

对于 \(noi\) 限制我们可以再加一维表示匹配到第几个。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=1005;
const int E=32780;
char S[N];
int trans[E][3],f[20],g[20];
int dp[3][E][5];
int ans[N],Popcount[E];
int rk[N];
int n,k,t;
void build(){
    t=(1ll<<k);
    for(int s=0;s<t;s++){
        for(int i=1;i<=k;i++) f[i]=((s>>(i-1))&1);
        for(int i=1;i<=k;i++) f[i]=f[i-1]+f[i];
        for(int c=0;c<=2;c++){
            for(int i=1;i<=k;i++) g[i]=f[i];
            for(int i=1;i<=k;i++) g[i]=max({g[i],f[i-1]+(rk[S[i]]==c),g[i-1]});
            int res=0;
            for(int i=1;i<=k;i++) res|=((g[i]-g[i-1])<<(i-1));
            trans[s][c]=res;
        }
    }
}
signed main(){
    scanf("%lld%lld",&n,&k);
    scanf("%s",S+1);
    rk['N']=0,rk['O']=1,rk['I']=2;
    build();
    for(int s=0;s<t;s++) Popcount[s]=Popcount[s>>1]+(s&1);
    int op=0;
    dp[0][0][0]=1;
    for(int i=1;i<=n;i++){
        for(int s=0;s<t;s++){
            for(int c=0;c<=2;c++){
                if(c==0){
                    dp[op^1][trans[s][c]][1]=(dp[op^1][trans[s][c]][1]+dp[op][s][0])%mod;
                    dp[op^1][trans[s][c]][1]=(dp[op^1][trans[s][c]][1]+dp[op][s][1])%mod;
                    dp[op^1][trans[s][c]][1]=(dp[op^1][trans[s][c]][1]+dp[op][s][2])%mod;
                }
                else if(c==1){
                    dp[op^1][trans[s][c]][2]=(dp[op^1][trans[s][c]][2]+dp[op][s][1])%mod;
                    dp[op^1][trans[s][c]][0]=(dp[op^1][trans[s][c]][0]+dp[op][s][0])%mod;
                    dp[op^1][trans[s][c]][0]=(dp[op^1][trans[s][c]][0]+dp[op][s][2])%mod;
                }
                else{
                    dp[op^1][trans[s][c]][0]=(dp[op^1][trans[s][c]][0]+dp[op][s][1])%mod;
                    dp[op^1][trans[s][c]][0]=(dp[op^1][trans[s][c]][0]+dp[op][s][0])%mod;
                }
            }
        }
        for(int s=0;s<t;s++) dp[op][s][0]=dp[op][s][1]=dp[op][s][2]=0;
        op^=1;
    }
    for(int s=0;s<t;s++){
        ans[Popcount[s]]=(ans[Popcount[s]]+dp[op][s][0]+dp[op][s][1]+dp[op][s][2])%mod;
    }
    for(int i=0;i<=k;i++){
        printf("%lld\n",ans[i]);
    }
}

优美的字符串

题面

首先考虑如何判断一个没有 \(?\) 的字符串是否合法,我们可以设 \(f(i,0/1,0/1)\) 表示假如第 \(i\) 位字符为 \(0/1\),前缀是否可以消成 \(0/1\)。

设 \(F(a,b,c)\) 表示这三个字符可以变成什么,考虑我们要最后字符变为 \(b\),我们可以考虑两种合并的方式

  • 一种是 \(s_{i-2},s_{i-1},s_{i}\) 合并后再与之前的合并,也就是判断 \(f(i-1,F(s_{i-2},s_{i-1},s_{i}),b)\) 是否符合。

  • 另一种是将 \(i-2\) 之前的合并玩之后再进行合并,那么转移就很显然。

然后就有如下暴力

code

for(int i=3;i<=n;i+=2){
    f[i][0][0] |= f[i-2][F[s[i-2]][s[i-1]][0]][0];
    f[i][0][1] |= f[i-2][F[s[i-2]][s[i-1]][0]][1];
    f[i][1][0] |= f[i-2][F[s[i-2]][s[i-1]][1]][0];
    f[i][1][1] |= f[i-2][F[s[i-2]][s[i-1]][1]][1];

    f[i][1][F[1][s[i-1]][1]] |= f[i-2][s[i-2]][1];
    f[i][1][F[0][s[i-1]][1]] |= f[i-2][s[i-2]][0];
    f[i][0][F[1][s[i-1]][0]] |= f[i-2][s[i-2]][1];
    f[i][0][F[0][s[i-1]][0]] |= f[i-2][s[i-2]][0];
}

当我们知道 \(s_{i-1},s_{i}\) (外层 dp 枚举),我们发现转移到 \(f(i)\) 只需要 \(f(i-2)\) (内层考虑)的状态,所以我们将 \(f(i)\) 的转态进行状压,具体就是维护五个数,分别是 \((0,1),(0,0)(1,1)(1,0),s_i\),然后提前预处理出来转移指针,也就是 \(trans(s,a,b)\) 表示在 \(s_i\) 后面加上 \(a,b\) 两个字符后转移到的状态,所以外层转移就是

\[dp_{i+2,trans(s,a,b)} \leftarrow dp_{i,s} \]

code
// ubsan: undefined
// accoders
// 优美的字符串 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int N=1e5+10;
char th[N],sh[N];
int t[N],s[N];
bool F[2][2][2];
int dp[N][50];
int trans[50][2][2];
bool las[2][2],now[2][2];
signed main(){
    int T;
    scanf("%lld",&T);
    while(T--){
        scanf("%s",th+1);
        scanf("%s",sh+1);
        int n=strlen(sh+1);
        for(int i=1;i<=8;i++) t[i]=th[i]-'0';
        for(int i=1;i<=n;i++) s[i]=sh[i]-'0';
        memset(dp,0,sizeof(dp));
        for(int i=0;i<8;i++){
            int a=i&1;
            int b=(i>>1)&1;
            int c=(i>>2)&1;
            F[a][b][c]=t[i+1];
        }
        int t=(1<<5);
        for(int S=0;S<t;S++){
            las[1][1]=(S>>1&1),las[1][0]=(S>>2&1),las[0][1]=(S>>3&1),las[0][0]=(S>>4&1);
            int ss=(S&1);
            for(int s1=0;s1<=1;s1++){
                for(int s2=0;s2<=1;s2++){
                    now[1][1]=0,now[1][0]=0,now[0][1]=0,now[0][0]=0;
                    now[0][0] |= las[F[ss][s1][0]][0];
                    now[0][1] |= las[F[ss][s1][0]][1];
                    now[1][0] |= las[F[ss][s1][1]][0];
                    now[1][1] |= las[F[ss][s1][1]][1];
                    now[1][F[1][s1][1]] |= las[ss][1];
                    now[1][F[0][s1][1]] |= las[ss][0];
                    now[0][F[1][s1][0]] |= las[ss][1];
                    now[0][F[0][s1][0]] |= las[ss][0];
                    trans[S][s1][s2]=(s2&1)|(now[1][1]<<1)|(now[1][0]<<2)|(now[0][1]<<3)|(now[0][0]<<4);
                }
            }
        }
        if(s[1]!=1) dp[1][1<<4]=1;
        if(s[1]!=0) dp[1][(1<<1)+1]=1;
        for(int i=1;i<n;i+=2){
            for(int S=0;S<t;S++){
                if(s[i]==((S&1)^1)) continue;
                for(int s1=0;s1<=1;s1++){
                    if(s[i+1]==(s1^1)) continue;
                    for(int s2=0;s2<=1;s2++){
                        if(s[i+2]==(s2^1)) continue;
                        dp[i+2][trans[S][s1][s2]]=(dp[i+2][trans[S][s1][s2]]+dp[i][S])%mod;
                    }
                }
            }
        }
        int ans=0;
        for(int s=0;s<t;s++){
            if(s&1){
                if(s>>1&1) ans=(ans+dp[n][s])%mod;
            }
            else{
                if(s>>3&1) ans=(ans+dp[n][s])%mod;
            }
        }
        printf("%lld\n",ans);
    }
}

标签:LCS,int,DP,trans,转移,dp,mathrm
From: https://www.cnblogs.com/jinjiaqioi/p/17998049

相关文章

  • 数位DP的一般方法
    数位DP?数位DFS!P2657[SCOI2009]windy数-洛谷|计算机科学教育新生态(luogu.com.cn)不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。windy想知道,在a和b之间,包括a和b,总共有多少个windy数?我们使用DFS解决。数位DFS要设计好状态,考虑好哪些条件会......
  • Nginx配置TCP/UDP流量转发
    #usernobody;worker_processes1;#error_loglogs/error.log;#error_loglogs/error.lognotice;#error_loglogs/error.loginfo;#pidlogs/nginx.pid;events{worker_connections1024;}stream{log_formatmain'$remote_addr[$tim......
  • leetcode--5. 最长回文子串(dp)
    记录23:292024-2-5https://leetcode.cn/problems/longest-palindromic-substring/dp[i][j]s[i,j]之间能否构成回文子串[i,j]之间是否能够构成需要考虑[i+1,j-1]是否构成回文子串且s[i]==s[j]当j-1>=i+1时候说明正好是俩个相邻的字符,此时如果s[i]==s[j]就肯定可......
  • DPDK-22.11.2 [六] RSS receive side scaling 网卡分流机制
    这个的作用就是为了提高性能。当分析网络数据时,可以为网口提供多个接收队列,每个cpu处理一个队列。如果每条队列是独立的,那么就可以很好的并发。这里有两个问题,一个是数据需要平均的分配到每个队列;二是同一组数据需要分配到同一个队列。rss就是这个作用,可以设定以ip进行区分,或......
  • 编译安装LAMP环境及wordpress部署
    一、安装背景及任务需求1.LAMP简介LAMP是公认的最常见、最古老的黄金Web技术栈Linux操作系统Apache/Nginxweb服务器作用是将HTTP请求从前端转发到后端应用上Mysql/MariadbMysql是一款数据库管理系统,也就是一个存储数据的工具,用户可以自行对数据库进行增加、删除......
  • SpringBoot中使用Spring自带线程池ThreadPoolTaskExecutor与Java8CompletableFuture实
    场景关于线程池的使用:Java中ExecutorService线程池的使用(Runnable和Callable多线程实现):https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/126242904Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例:https://blog.csdn.net/BADAO_......
  • R语言Kmeans聚类、PAM、DBSCAN、AGNES、FDP、PSO粒子群聚类分析iris数据结果可视化比
    全文链接:http://tecdat.cn/?p=32007原文出处:拓端数据部落公众号本文以iris数据和模拟数据为例,帮助客户了比较R语言Kmeans聚类算法、PAM聚类算法、DBSCAN聚类算法、AGNES聚类算法、FDP聚类算法、PSO粒子群聚类算法在iris数据结果可视化分析中的优缺点。结果:聚类算法的聚类结......
  • 2/4总结:DP
    数位dp:求区间【l,r】内有多少满足条件的数windy数记忆化递归搜索+处理限制条件前缀优化:solve(r)-solve(l-1);-->包括边界限制条件之区间内:判断前面所有位数是否都为上界        limit&&(i==maxn)?=1限制条件之前导零:判断前面所有位数是否......
  • DotNetty 封装的 UdpClient
    DotNetty资料较少,UdpClient和TcpClient略有不同publicclassUdpCommunicator:ICommunicator{privateIChannel?_ClientChannel;privateBootstrap?_Bootstrap;IEventLoopGroup?_LoopGroup;privateTaskCompletionSource<byte[]>_ResponseComp......
  • 数位dp笔记
    数位dp学习笔记数位dp的问题题型一般是给定一个闭区间[L,R],求这个区间中满足“某种条件”的数的个数的总数对于这类问题,我们首先统计[L,R]范围的满足条件的数字转化成统计[1,N]内满足条件的数字的数量那么ans[L,R]=ans[1,R]-ans[1,L-1];先将n转换成字符串str,使用记忆化搜索......