zoukankan      html  css  js  c++  java
  • luogu 动态规划口胡合集

    P1831 杠杆数

    是否说出正解: ×

    显然是个数位DP,但是感觉无从下手,事实上可以做18次数位dp,每次枚举支点,就可以算出每个数的力矩,取出力矩为0的作为贡献即可

    #include <map>
    #include <set>
    #include <ctime>
    #include <cmath>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <string>
    #include <bitset>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <sstream>
    #include <iostream>
    #include <algorithm>
    #include <functional>
    using namespace std;
    #define For(i, x, y) for(int i=x;i<=y;i++)  
    #define _For(i, x, y) for(int i=x;i>=y;i--)
    #define Mem(f, x) memset(f,x,sizeof(f))  
    #define Sca(x) scanf("%d", &x)
    #define Sca2(x,y) scanf("%d%d",&x,&y)
    #define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
    #define Scl(x) scanf("%lld",&x)  
    #define Pri(x) printf("%d
    ", x)
    #define Prl(x) printf("%lld
    ",x)  
    #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
    #define LL long long
    #define ULL unsigned long long  
    #define mp make_pair
    #define PII pair<int,int>
    #define PIL pair<int,long long>
    #define PLL pair<long long,long long>
    #define pb push_back
    #define fi first
    #define se second 
    typedef vector<int> VI;
    LL read(){LL x = 0,f = 1;char c = getchar();while (c<'0' || c>'9'){if (c == '-') f = -1;c = getchar();}
    while (c >= '0'&&c <= '9'){x = x * 10 + c - '0';c = getchar();}return x*f;}
    const double PI = acos(-1.0);
    const double eps = 1e-9;
    const int maxn = 110;
    const int INF = 0x3f3f3f3f;
    const int mod = 1e9 + 7; 
    int N,M,K,now;
    int str[20];
    LL dp[20][3000];
    LL dfs(int pos,int val,int limit){
        if(!pos) return val == 0;
        if(val < 0) return 0;
        if(!limit && ~dp[pos][val]) return dp[pos][val];
        LL ans = 0;
        int up = limit?str[pos]:9;
        for(int i = 0 ; i <= up; i ++){
            ans += dfs(pos - 1,val + i * (pos - now),limit && (i == up));
        }
        if(!limit) dp[pos][val] = ans;
        return ans;
    }
    LL solve(LL x){
        if(!x) return 1;
        int l = 0;
        LL m = x;
        while(m){
            l++; str[l] = m % 10;
            m /= 10;
        }
        LL ans = 0;
        for(int i = 1; i <= l; i ++){
            Mem(dp,-1);
            now = i;
            ans += dfs(l,0,1);
        }
        return ans - (l - 1); 
    }
    int main(){
        LL l = read(),r = read();
        Prl(solve(r) - solve(l - 1));
        return 0;
    }
    View Code

    P2939 [USACO09FEB]改造路Revamping Trails

    是否说出正解:√

    很原题了 最短路维护dis[i][j]表示到i点已经经过了j条高速公路的最短路, Dijkstra直接维护即可

    P2198 杀蚂蚁

    是否说出正解:√

    似乎dp[x][y]表示经过了x个放射塔,y个放射塔的最小值即可,干扰塔数量用n - x - y表示

    dp[x][y] = min(dp[x - 1][y] + (x - 1) * g * (t + y * b),dp[x][y - 1] + x * g  * (t + (y - 1) * b,dp[x][y] + (x + r) * g * (t + y * b) 

    P4766 [CERC2014]Outer space invaders

    是否说出正解: ×

    离散化之后区间dp

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 6010;
    const int INF = 0x3f3f3f3f;
    int N;
    struct Node{
        int l,r,d;
        Node(int l = 0,int r = 0,int d = 0):l(l),r(r),d(d){}
    }node[maxn];
    int Hash[maxn];
    int dp[maxn][maxn];
    int main(){
        int T; scanf("%d",&T);
        while(T--){
            scanf("%d",&N); int cnt = 0;
            for(int i = 1; i <= N; i ++){
                scanf("%d%d%d",&node[i].l,&node[i].r,&node[i].d);
                Hash[++cnt] = node[i].l; Hash[++cnt] = node[i].r;
            }
            sort(Hash + 1,Hash + 1 + cnt);
            cnt = unique(Hash + 1,Hash + 1 + cnt) - Hash - 1;
            for(int i = 1; i <= N ; i ++){
                node[i].l = lower_bound(Hash + 1,Hash + 1 + cnt,node[i].l) - Hash;
                node[i].r = lower_bound(Hash + 1,Hash + 1 + cnt,node[i].r) - Hash;
            }
            for(int len = 1; len <= cnt; len ++){
                for(int l = 1; l + len - 1 <= cnt; l ++){
                    int r = l + len - 1;
                    int id = 0;
                    for(int i = 1; i <= N ; i ++){
                        if(node[i].l < l || node[i].r > r) continue;
                        if(!id || node[id].d < node[i].d) id = i;
                    }
                    if(!id){
                        dp[l][r] = 0;
                        continue;
                    }
                    dp[l][r] = INF;
                    for(int k = node[id].l; k <= node[id].r; k ++){
                        dp[l][r] = min(dp[l][r],dp[l][k - 1] +dp[k + 1][r] + node[id].d);
                    }
                }
            }
            printf("%d
    ",dp[1][cnt]);
        }
        return 0;
    }
    View Code

    P2400 秘密文件

    是否说出正解:√

    利用KMP的next数组求出每个子串的循环节,然后区间dp

    P4046 [JSOI2010]快递服务

    是否说出正解:√

    从给定的顺序开始递推,dp[i][j]表示除了一个司机必定在上一个点之外,其他两个司机分别在第i,j的位置,因为每一层之间独立,所以需要开滚动数组

    时间复杂度O(n³)

    P3413 SAC#1 - 萌数

    是否说出正解:√

    显然是数位dp,意识到最短回文串只需要关注两位数字和三位数字即可,即存在一个数和前一位或者前前一位相等即可。

    那么dp[1000][11][11]表示当前位置pos上一位数字为i,上上位数字为j有多少种情况然后去记忆化搜索即可。

    因为数位dp并不熟练,所以敲了一发

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    const int maxn = 1010;
    const int mod = 1e9 + 7;
    char s1[maxn],s2[maxn];
    int str[maxn];
    LL dp[maxn][11][11];
    LL ten[maxn],num[maxn];
    int L;
    LL dfs(int pos,int la,int lla,bool limit,bool zero){
        if(pos == L) return 0;
        if(~dp[pos][la][lla] && !limit && !zero) return dp[pos][la][lla];
        int top = limit?str[pos]:9;
        LL ans = 0;
        for(int i = 0; i <= top; i ++){
            if(i == la || i == lla){
                if(limit && i == str[pos]){
                     ans = (ans + num[pos + 1] + 1) % mod;
                    // cout << pos << " " << num[pos + 1] << endl;
                }
                else ans = (ans + ten[pos]) % mod;
                continue;
            }
            ans = (ans + dfs(pos + 1,(zero && !i)?10:i,la,limit && i == str[pos],zero && !i)) % mod;
        }
        if(!limit && !zero) dp[pos][la][lla] = ans;
        return ans;
    }
    LL solve(char *s){
        int l = strlen(s); L = l;
        ten[l - 1] = 1; num[l] = 0;
        for(int i = l - 1; i >= 0; i --){
             str[i] = s[i] - '0';
             num[i] = (num[i + 1] + (str[i] * ten[i]) % mod) % mod;
             if(i) ten[i - 1] = ten[i] * 10 % mod;
        }
        for(int i = 0 ; i <= l; i ++){
            for(int j = 0 ; j <= 10; j ++) for(int k = 0 ; k <= 10; k ++) dp[i][j][k] = -1;
        }
        return dfs(0,10,10,1,1);
    }
    
    int check(char *s){
        int l = strlen(s);
        for(int i = 1; i < l; i ++){
            if(s[i] == s[i - 1]) return true;
            if(i > 1 && s[i] == s[i - 2]) return true;
        }
        return false;
    }
    int main(){
        scanf("%s%s",s1,s2);
    
        printf("%lld
    ",(solve(s2)-solve(s1) + check(s1) + mod) % mod);
        return 0;
    }
    View Code

    P4290 [HAOI2008]玩具取名

    比较裸的区间dp dp[l][r][x]表示区间[l,r]合成字母x的可行性

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 220;
    int N;
    const char c[5] = {'0','W','I','N','G'};
    map<char,int>id;
    int num[5];
    char str[5];
    char s[maxn];
    bool change[5][5][5];
    bool dp[maxn][maxn][5];
    int main(){
        id['W'] = 1; id['I'] = 2; id['N'] = 3; id['G'] = 4;
        for(int i = 1; i <= 4; i ++) scanf("%d",&num[i]);
        for(int i = 1; i <= 4; i ++){
           for(int j = 1; j <= num[i]; j ++){
                scanf("%s",str);
                change[id[str[0]]][id[str[1]]][i] = 1;
           }
        }
        scanf("%s",s + 1); int L = strlen(s + 1);
        for(int i = 1; i <= L ; i ++) dp[i][i][id[s[i]]] = 1;
        for(int len = 2; len <= L; len++){
            for(int l = 1; l + len - 1<= L; l ++){
                int r = l + len - 1;
                for(int k = l; k < r; k ++){
                    for(int p = 1; p <= 4; p ++){
                        if(!dp[l][k][p]) continue;
                        for(int q = 1; q <= 4; q ++){
                            if(!dp[k + 1][r][q]) continue;
                            for(int x = 1; x <= 4; x ++){
                                if(change[p][q][x]) dp[l][r][x] = 1;
                            }
                        }
                    }
                }
            }
        }
        bool flag = 0;
        for(int i = 1; i <= 4; i ++) if(dp[1][L][i]){
            putchar(c[i]);
            flag = 1;
        } 
        if(!flag) puts("The name is wrong!");
        
        return 0;
    }
    View Code

    P1121 环状最大两段子段和

    枚举中间点,求中间点往左的最大字段和加上中间点往右的最大子段和的最大值。

    如果字段经过了环,就用将字段取反,用同样的方法求两段最大值,表示这两段不取。

  • 相关阅读:
    java 中 堆、栈的区别(转)
    斐波那契数列(关于递归)
    .NetCore使用Hangfire
    大话西游系统文件分析
    VC游戏开发图片镂空
    华硕XTion Pro开发环境配置
    TweenMax 参数说明(中文翻译)
    程序员的追求
    最近得到的
    mvc的json
  • 原文地址:https://www.cnblogs.com/Hugh-Locke/p/11681770.html
Copyright © 2011-2022 走看看