战胜序列 [性质dp]

战 胜 序 列 战胜序列



正 解 部 分 \color{red}{正解部分}

F [ i , a , b , c ] F[i, a, b, c] F[i,a,b,c] 表示前 i i i 项 , 分别以 0 , 1 , 2 0,1,2 0,1,2 为结尾的 最长战胜序列 长度为 a , b , c a, b, c a,b,c 的方案数,
枚举状态, O ( 3 ) O(3) O(3) 转移, 总复杂度 O ( N 4 ) O(N^4) O(N4) .

可以发现 0 0 0 结尾的 最长上升序列 去掉 0 0 0 后得到长度为 b − 1 b-1 b1 的以 2 2 2 序列结尾的序列,
b ≥ a − 1 b \geq a-1 ba1, 同理 c ≥ b − 1 c \geq b-1 cb1, a ≥ c − 1 a \geq c-1 ac1 ,可得 ∣ b − a ∣ ≤ 2 |b-a| \le 2 ba2, ∣ c − a ∣ ≤ 2 |c-a| \le 2 ca2 .

于是设 F [ i , a , b , c ] F[i, a, b, c] F[i,a,b,c] 表示前 i i i 项, 分别以 0 , 1 , 2 0,1,2 0,1,2 为结尾的 最长战胜序列 长度为 a , a + b − 2 , a + c − 2 a, a+b-2, a+c-2 a,a+b2,a+c2 的方案数, 转移即可, 时间复杂度 O ( N 2 ) O(N^2) O(N2) .


实 现 部 分 \color{red}{实现部分}

使用状态去更新状态, 注意在 a a a 变化时, b , c b,c b,c 也要随着变化 .

#include<bits/stdc++.h>
#define reg register

const int maxn = 2005;
const int mod = 998244353;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ c = getchar(), flag = -1; break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

int N;
int Ans[maxn];
int F[maxn][maxn][5][5];

bool vis[maxn][3];

char Smp[5];

int main(){
        N = read();
        for(reg int i = 1; i <= N; i ++){
                scanf("%s", Smp);
                int len = strlen(Smp);
                for(reg int j = 0; j < len; j ++) vis[i][Smp[j]-'0'] = 1;
        }
        F[0][0][2][2] = 1;
        for(reg int i = 0; i < N; i ++)
                for(reg int j = 0; j <= i; j ++)
                        for(reg int a = 0; a <= 4; a ++)
                                for(reg int b = 0; b <= 4; b ++){
                                        if(j+a-2 < 0 || j+b-2 < 0 || !F[i][j][a][b]) continue ;
                                        int len_1 = j+a-2, len_2 = j+b-2;
                                        if(vis[i+1][0]){
                                                int nl = std::max(j, len_2+1);
                                                int &t = F[i+1][nl][len_1-nl+2][len_2-nl+2];
                                                t = (t + F[i][j][a][b]) % mod;
                                        }
                                        if(vis[i+1][1]){
                                                int nl = std::max(j+1, len_1);
                                                int &t = F[i+1][j][nl-j+2][b];
                                                t = (t + F[i][j][a][b]) % mod;
                                        }
                                        if(vis[i+1][2]){
                                                int nl = std::max(len_2, len_1+1);
                                                int &t = F[i+1][j][a][nl-j+2];
                                                t = (t + F[i][j][a][b]) % mod;
                                        }
                                }
        for(reg int i = 0; i <= N; i ++)
                for(reg int a = 0; a <= 4; a ++)
                        for(reg int b = 0; b <= 4; b ++){
                                int t = std::max(i, std::max(i+a-2, i+b-2));
                                printf("%d\n", F[N][i][a][b]);
                                Ans[t] = (Ans[t] + F[N][i][a][b]) % mod;
                        }
        for(reg int i = 1; i <= N; i ++) printf("%d ", Ans[i]);
        return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值