DP经典例题1

目录

1.Atcoder261D

2.牛客多校竞赛第1场I题(概率DP)

3.Acwing1090绿色通道(单调队列优化dp + 二分答案)

4.Atcoder265E


1.Atcoder261D

        (1)题目描述

        (2)题目分析:给定你两个序列,第一个序列是第i场硬币朝上你能获得的奖励,第二个序列是你连续i场向上你能获得的奖励,现在问你丢n次硬币,能获得的最大奖励是多少。

        (3)解题思路:我们可以考虑dp,

        dp[i][j] 表示从我当前是第i场,前j场连胜。

        那么我们很容易退出状态转移方程

        处理边界情况

                        1.j < i : dp[i][0] =  max(dp[i][0],dp[i - 1][j]);                                                        

                        2.j != 0: dp[i][j] = max(dp[i][j],dp[i - 1][j - 1] + w1[i] + w2[j]); 

        (4)代码实现:

const int N = 5010;
long long dp[N][N],w1[N],w2[N];
int main()
{
    int n,m;
    cin >> n >> m;
    for(int i = 1;i <= n;i++) cin >> w1[i];
    for(int i = 1;i <= m;i++) {
        int u,v;
        cin >> u >> v;
        w2[u] = v;
    }
    for(int i = 1;i <= n;i++) {
        for(int j = 0;j <= i;j++) {
            if(j < i) dp[i][0] = max(dp[i][0],dp[i - 1][j]);
            if(j != 0) dp[i][j] = max(dp[i][j],dp[i - 1][j - 1] + w1[i] + w2[j]);
        }
    }
    long long res = 0;
    for(int i = 0;i <= n;i++) res = max(dp[n][i],res);
    cout << res << endl;
    return 0;
}

2.牛客多校竞赛第1场I题(概率DP)

        (1) 题目描述

 当时没怎么学过概率Dp,这个题也就很顺利的没有当场写出来,后面知道是板子题。。。。

        (2)题意分析:给你一副麻将,问你这把牌凑成七对的次数是多少。

        (3)解题思路:dp[i][j]表示牌库还有i张牌,当前手上还有j张单牌的方案数

        下一张牌可以凑对:dp[i][j] = 3 * j / i * (dp[i - 1][j - 2] + 1)

        下一张牌不可凑对:dp[i][j] = (i - 3 * j) / i * dp[i - 1][j] + 1)

因此dp[i][j] = 3 * j / i * (dp[i - 1][j - 2] + 1) + dp[i][j] = (i - 3 * j) / i * dp[i - 1][j] + 1)

处理边界情况j == 0 直接return 0

处理边界情况j == 1 dp[i][j] = (i - 3 * j * (dp[i - 1][j] + 1))  + 3 * j / i * 1) 

        (4)代码实现:

#include "bits/stdc++.h"
using namespace std;
map<string,int> mp;
long long dp[150][20];
const int mod = 1e9 + 7;
long long qpow(long long a,int p)
{
    long long res = 1;
    while(p) {
        if(p & 1) res = res * a % mod;
        a = a * a % mod;
        p >>= 1;
    }
    return res;
}
long long dfs(int i,int j)
{
    if(dp[i][j] != -1) return dp[i][j];
    if(j == 0) return 0;
    long long p1 = (i - 3 * j) * qpow(i,mod - 2) % mod;
    long long p2 = (3 * j) * qpow(i,mod - 2) % mod;
    if(j == 1) dp[i][j] = (p1 * (dfs(i - 1,j) + 1) % mod + p2 * 1 % mod) % mod;
    else dp[i][j] = ((p1 * (dfs(i - 1,j) + 1) % mod + p2 * (dfs(i - 1,j - 2) + 1) % mod)) % mod; 
    return dp[i][j];
}   
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    memset(dp,-1,sizeof(dp));
    int T,cas = 1;
    cin >> T;
    for(;cas <= T;cas++) {
        int n = 0;
        string s;
        cin >> s;
        mp.clear();
        for(int i = 0;i < 13;i++) {
            string p = "";
            p += s[2 * i];
            p += s[2 * i + 1];
            mp[p]++;
        }
        for(auto x:mp) n += (x.second == 1);
        cout << "Case #" << cas << ": " << dfs(123,n) << endl;
    }
    return 0;
}

3.Acwing1090绿色通道(单调队列优化dp + 二分答案)

        (1)题目描述

        (2)题意分析:给定你一个序列,问你现在能用不超过t分钟,并且空题段最长。

        (3)解题思路:我们直接二分空题段的长度,每次都进行一次check操作,check操作使用单调队列优化dp,看能否达到t分钟内并且空段是mid的情况下成立。

        (4)代码实现:

#include "bits/stdc++.h"
using namespace std;
const int N = 50010;
int f[N],q[N],w[N];
int n,m;
bool check(int k)
{
    int hh,tt;
    hh = tt = 0;
    f[0] = 0;
    for(int i = 1;i <= n;i++) {
        //因为第i-k-1段能用到,因此需要判一下
        if(hh <= tt && q[hh] < i - k - 1) hh++;
        f[i] = w[i] + f[q[hh]];
        while(hh <= tt && f[i] <= f[q[tt]]) tt--;
        q[++tt] = i;
    }
    for(int i = n - k;i <= n;i++) if(f[i] <= m) return true;
    return false;
}
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++) cin >> w[i];
    int l = 0,r = n;
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(check(mid)) r = mid - 1;
        else l = mid + 1;
    }
    cout << l << endl;
    return 0;
}

4.Atcoder265E

        (1)题目描述

        (2)题意分析

                这个题给定我们三个操作,从0,0开始,不能走到非法点上的最后不同的路径的条数。

        (3)解题思路

                对于这三个操作,我们分析题目条件,最多不超过300次,因此我们可以枚举两个操作,然后算出另一个操作的次数。仅需进行转移即可。

        (4)代码实现

#include "bits/stdc++.h"
#define PII pair<int,int>
using namespace std;
const int N = 3e2 + 10,mod = 998244353;
map <PII,bool> mp;
int dp[N][N][N];
int ans = 0;
void solve()
{
    int n,m;
    int a,b,c,d,e,f;
    cin >> n >> m;
    cin >> a >> b >> c >> d >> e >> f;
    for(int i = 1;i <= m;i++) {
        int x,y;
        cin >> x >> y;
        mp[{x,y}] = true;
    }
    dp[0][0][0] = 1;
    for(int i = 1;i <= n;i++) {
        for(int j = 0;j <= i;j++) {
            for(int k = 0;k <= i;k++) {
                if(k + j > i) break;
                //枚举第三个操作的数量                
                int q = i - j - k; 
                int x = j * a + k * c + q * e;
                int y = j * b + k * d + q * f;
                if(mp.count({x,y})) continue;
                if(!mp.count({x - a,y - b}) && j) dp[j][k][q] = (dp[j][k][q] + dp[j - 1][k][q]) % mod;
                if(!mp.count({x - c,y - d}) && k) dp[j][k][q] = (dp[j][k][q] + dp[j][k - 1][q]) % mod;
                if(!mp.count({x - e,y - f}) && q) dp[j][k][q] = (dp[j][k][q] + dp[j][k][q - 1]) % mod;
                if(i == n) ans = (ans + dp[j][k][q]) % mod;
            }
        }
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T;
    T = 1;
    while(T --) {
        solve();
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值