洛谷题单指南-动态规划2-P4933 大师

原题链接:https://www.luogu.com.cn/problem/P4933

题意解读:求有多少个子序列可以组成等差序列

解题思路:

1、暴力DFS

如果实在想不出动规的方法,对于n<=20的数据,可以DFS枚举所有子序列的子集,再判断是否是等差数列。

30分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1000, MOD = 998244353;
int h[N], n, ans;
int a[N], b[N]; //a对应的h选则填1,不选则填0;b存选中的数

void dfs(int k)
{
    if(k > n)
    {
        //判断是否是等差数列
        int cnt = 0;
        for(int i = 1; i <= n; i++)
        {
            if(a[i]) b[++cnt] = h[i];
        }
        if(cnt == 0) return;
        if(cnt == 1 || cnt == 2) ans = (ans + 1) % MOD;
        else
        {
            for(int i = 3; i <= cnt; i++)
            {
                if(b[i] - b[i-1] != b[i-1] - b[i-2]) return;
            }
            ans = (ans + 1) % MOD;
        }
        
        return;
    }
    
    for(int i = 0; i <= 1; i++) //第k个可以选或者不选
    {
        a[k] = i;
        dfs(k + 1);
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> h[i];
    dfs(1);
    cout << ans;

    return 0;
}

2、动态规划1

设dp[i][j]表示以i,j为最后两位的等差数列个数,状态如何转移?

枚举i前面的所有k,只要h[i]-h[k] == h[j]-h[i],则可以从dp[k][i]转移到dp[i][j],dp[i][j] += dp[k][i]即可,注意取模

对于dp[][]的初始化,对于每一对dp[i][j],初始值都dp[i][j]=1,也就是两个数也是一组等差数列

把所有dp[i][j]加起来即可,另外因为一个数也是等差数列,答案再加上n

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1000, MOD = 998244353;
int h[N], n, ans;
int dp[N][N]; //dp[i][j]表示以i,j为最后两位的等差数列个数

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> h[i];

    for(int j = 2; j <= n; j++)
    {
        for(int i = 1; i < j; i++)
        {
            dp[i][j] = 1;  //i,j组成的两位序列也是一个等差序列
            for(int k = 1; k < i; k++)
            {
                if(h[i] - h[k] == h[j] - h[i]) 
                    dp[i][j] = (dp[i][j] + dp[k][i]) % MOD;
            }
            ans = (ans + dp[i][j]) % MOD;
        }
    }
    ans = (ans + n) % MOD; //加上长度为1的序列个数
    cout << ans;

    return 0;
}

3、动态规划2

设dp[i][j]表示以i结尾,公差为j的等差数列个数,状态如何转移?

枚举i前面的所有k,计算公差h[i]-h[k],直接基于公差h[i]-h[k]进行转移dp[i][h[i]-h[k]] += dp[k][h[i]-h[k]]

由于公差可能为负数,塔高1~20000,h[i]-h[k]要加上20000可以确保正数:dp[i][h[i]-h[k]+20000] += dp[k][h[i]-h[k]+20000]

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1005, V = 20000, M = 40005, MOD = 998244353;
int h[N], n, ans;
int dp[N][M]; //dp[i][j]表示以i结尾,公差为j的等差数列个数,状态如何转移?

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> h[i];

    for(int i = 2; i <= n; i++)
    {
        for(int k = 1; k < i; k++)
        {
            dp[i][h[i] - h[k] + V] += dp[k][h[i] - h[k] + V] + 1; //k,i两个数也可以组成一个等差数列,所以要加上1
            dp[i][h[i] - h[k] + V] %= MOD;
        }
    }
    for(int i = 2; i <= n; i++)
    {
        for(int j = 1; j <= M; j++)
        {
            ans = (ans + dp[i][j]) % MOD;
        }
    }
    ans = (ans + n) % MOD;
    cout << ans;

    return 0;
}

 

posted @ 2024-04-25 11:30  五月江城  阅读(69)  评论(0编辑  收藏  举报