线性Dp习题集

一.Problem - F - Codeforces

        (1)题目大意

                给定你一个字符串长度为n,里面包含a,b,c,?四种符号,?号可以代替三种符号,现在问你最后构成的abc能够有多少。

         (2)解题思路

                对于每一个串,如果可以构成abc,那么一定是从从左到右的,那么我们可以线性递推过去,对于abc这三种字符,b能够构成有效的序列是前面的a连着,c能够构成有效的序列是前面的b连着,而对于?号,可以构成abc三类字符,因此对于每一个?号,对答案的贡献肯定是3*dp[j] + dp[j - 1]。

                比如说abcabc?,最后这个问号不论是a还是b还是c都会让答案加上一倍,但是c会让答案多加上一次前面含b的,因此状态转移方程就是dp[j] = (3 * dp[j] + dp[j - 1])。

        (3)代码实现

#include "bits/stdc++.h"
using namespace std;
const int N = 2e5 + 10,mod = 1e9 + 7;
long long dp[4];
void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    s = " " + s;
    dp[0] = 1;
    for(int i = 1;i < s.size();i++) {
        if(s[i] == 'a') dp[1] = (dp[1] + dp[0]) % mod;
        else if(s[i] == 'b') dp[2] = (dp[2] + dp[1]) % mod;
        else if(s[i] == 'c') dp[3] = (dp[2] + dp[3]) % mod;
        else {
            for(int j = 3;j >= 1;j--) dp[j] = (3LL * dp[j] + dp[j - 1]) % mod;
            dp[0] = 3LL * dp[0] % mod;
        } 
    }
    cout << dp[3] << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T;
    T = 1;
    while(T --) {
        solve();
    }
    return 0;
}

二.Problem - E - Codeforces

        (1)题目大意

        给定你一个长度为n的密码序列,规定第对于每个段,它的长度被写在它的旁边,要么在它的左边,要么在它的右边,现在问你这个序列是否正确。

         (2)解题思路

                对于这个问题,我们不能直接做,而是要转换思路,看看每个数若是一个长度数,那哪些序列会被他包括进去,因此对于第i个数,我们可以向左或者向右走a[i]下,表示这是他的一个合法序列,然后看他们是否能够连接上一个操作数也就是a[i] - 1或者a[i] + 1的位置,若是可以,则这个数可以作为一个长度数,因此对于这个问题我们就抽象成了看能否从0出发通过这些序列走到n。
         
(3)代码实现

#include "bits/stdc++.h"
using namespace std;
const int N = 2e5 + 10;
int dp[N],a[N];
void solve()
{
    int n,x;
    cin >> n;
    memset(dp,0,sizeof(dp));
    dp[0] = dp[n + 1] = 1;
    for(int i = 1;i <= n;i++) {
        cin >> a[i];
        if(i - a[i] - 1 >= 0) dp[i] |= dp[i - a[i] - 1];
        if(i + a[i] <= n) dp[i + a[i]] |= dp[i - 1];
    }
    if(dp[n]) cout << "YES" << endl;
    else cout << "NO" << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T;
    cin >> T;
    while(T --) {
        solve();
    }
    return 0;
}

三.Problem - E - Codeforces

        (1)题目大意

                给定你六个数,分别是技能1造成的伤害,以及冷却时间;技能2造成的伤害,以及冷却时间,怪兽的血量还有怪兽的防御力,每次你对怪兽的攻击伤害为技能1-防御力,或者技能2-防御力,或者技能1+技能2-防御力。最后问你打死这只怪兽最少需要多少时间。

         (2)解题思路

                1.考虑单独A怪兽一下,你能造成技能1-防御力,或者技能2-防御力

                2.考虑前j - 1下用技能1A怪兽,第j下一起A怪兽。

                3.考虑前j - 1下用技能2A怪兽,第j下一起A怪兽。

                把这三个状态转移方程写出来,这个题就做完了。

         (3)代码实现

#include "bits/stdc++.h"
using namespace std;
using ll = long long;
const int N = 5010;
ll dp[N];
void solve()
{   
    memset(dp,0x3f,sizeof(dp));
    ll p1,t1,p2,t2,h,s;
    cin >> p1 >> t1 >> p2 >> t2 >> h >> s;
    dp[0] = 0;
    for(int i = 1;i <= h;i++) {
        dp[i] = min(dp[max(0LL,i - (p1 - s))] + t1,dp[max(0LL,i - (p2 - s))] + t2);
        for(int j = 1;j <= i;j++) {
            if(j * t1 >= t2) {
                ll attack = (j - 1) * (p1 - s) + (j * t1 - t2) / t2 * (p2 - s) + (p1 + p2 - s);
                dp[i] = min(dp[i],dp[max(0LL,i - attack)] + j * t1);
            }
            if(j * t2 >= t1) {
                ll attack = (j - 1) * (p2 - s) + (j * t2 - t1) / t1 * (p1 - s) + (p1 + p2 - s);
                dp[i] = min(dp[i],dp[max(0LL,i - attack)] + j * t2);
            }
        }
    }
    cout << dp[h] << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T;
    T = 1;
    while(T --) {
        solve();
    }
    return 0;
}

四.Problem - F - Codeforces

        (1)题目大意

                给你一个[l,r]的区间,每一次给每一个位数+1,现在问你会改变多少位。

         (2)解题思路

                对于每一位不是9的来说,会产生1的贡献,若为9则会产生2的贡献。我们计算这一位对高位产生了多少贡献即可,最后每一步加上来即可。

        (3)代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define ll long long
#define db double
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
using namespace std;
const int N = 1e5 + 10;
void solve()
{
    int l,r;
    cin >> l >> r;
    int ans = 0,step;
    string s1 = to_string(l),s2 = to_string(r);
    s1 = string(s2.size() - s1.size(),'0') + s1;
    step = r - l;
    for(int i = s1.size() - 1;i >= 0;i--) {
        ans += step;
        step = (step + s1[i] - '0') / 10;
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    cin >> T;
    while(T --) solve();
    return 0;
}

五.Problem - C - Codeforces

        (1)题目大意

                给你n次操作,一个m,问你m最后能变成多少位。

         (2)解题思路

                很明显我们可以预处理出操作0-9i次会变成多少位,然后加上就可以了。

                        对于操作次数i + 数位j<10,那么dp[i][j] = 1;

                        否则dp[i][j] = dp[i - (10 - j)][0] + dp[i - (10 - j)][1];//就是在第i-(10-j)这次我们一个1加上一个0转移过来的。

        (3)代码实现

#include "bits/stdc++.h"
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define ll long long
#define db double
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
using namespace std;
const int N = 2e5 + 10,mod = 1e9 + 7;
int dp[N][10];
void init()
{
    for(int i = 0;i <= 200000;i++)
        for(int j = 0;j <= 9;j++)
            if(j + i >= 10) dp[i][j] = (dp[i - (10 - j)][0] + dp[i - (10 - j)][1]) % mod;
            else dp[i][j] = 1;
}
void solve()
{
    int n,m;
    cin >> n >> m;
    int ans = 0;
    while(n) {
        ans = (ans + dp[m][n % 10]) % mod;
        n /= 10;
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    init();
    int T = 1;
    cin >> T;
    while(T --) solve();
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值