DP:状态转移的设计

如何得出一个较为合理的转移方程

总结

DP之所以难,主要在于其不固定,每一个转移方程的推导方式与转移方式都不一样,需要结合具体的题目来看

E - Count Arithmetic Subsequences

地址

题目要求等差数列的个数,我们知道确定一个等差数列需要首项、公差、项数,在本题中还多了一个量————位置

因为询问了不同长度的等差数列的个数,因此必须要项数这一维

同时本题数量较少而值域较大,并且因为不同下标的相同等差数列视为不同

考虑从下标入手,等差数列的第一项、第二项以及项数同样可以确定一个等差数列

具体到转移,一个长度为$i$的等差数列一定是从长度为$i-1$的等差数列转移而来,因此在转移时枚举前面公差相同的等差数列

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void SINGLE_TEST() 
{
    int n;cin>>n;
    vector<int> a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    // 第二项为i,第一项为j,项数为k的等差数列的个数
    vector<vector<vector<Z>>> dp(N+1,vector<vector<Z>>(N+1,vector<Z>(N+1,0)));
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            dp[i][j][2]+=1;
            for(int q=1;q<j;q++){
                for(int k=3;k<=i;k++){
                    if(a[i]-a[j]==a[j]-a[q])
                        dp[i][j][k]+=dp[j][q][k-1];
                }
            }
        }
    }
    vector<Z> ans(n+1);
    ans[1]=n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            for(int k=2;k<=i;k++){
                ans[k]+=dp[i][j][k];
            }
        }
    }
    for(int i=1;i<=n;i++){
        cout<<ans[i]<<" \n"[i==n];
    }
}

E - Alphabet Tiles

地址

这是一种前i个所能达成的答案的题型,比较经典,但是如果没有想到这方面可能就比较难设计出正确的dp状态

在此基础上,本题我们考虑$dp[i][j]$为前$j$个字母、长度为$i$的方案数

对于递推关系,考虑推到前$j$个字母、长度为$i$的情形,从前$j-1$个字母、长度为$s(s \leqslant i)$的情形递推,第$j$个字母,可以在已有的字符串中随意插入,插的数量则是$i-s$,方案数即为$C_{i}^{i-s}$,可以这样想,$i$个位置先什么都不放,把那些差值个数的字母插入后再放入原先的字母

代码中枚举的是差值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void SINGLE_TEST() 
{
    int k;cin>>k;
    vector<int> cnt(27);
    for(int i=1;i<=26;i++){
        cin>>cnt[i];
    }
    // 前c个字母,组成长度为i的字符串的方案数
    vector<vector<Z>> dp(k+1,vector<Z>(26+1,0));
    for(int i=0;i<=26;i++) dp[0][i]=1;

    for(int c=1;c<=26;c++){
        for(int i=1;i<=k;i++){
            for(int j=0;j<=min(i,cnt[c]);j++){
                dp[i][c]+=dp[i-j][c-1]*C(i,j);
            }
        }
    }
    Z ans=0;
    for(int i=1;i<=k;i++){
        ans+=dp[i][26];
    }   
    cout<<ans<<"\n";
}
Licensed under CC BY-NC-SA 4.0
最后更新于 Aug 05, 2024 00:00 UTC
本博客已稳定运行
使用 Hugo 构建
主题 StackJimmy 设计