• dp专场的蒟蒻题解


    前言:一直想练练dp,正好衣神弄了个训练赛。。上次看cgold大佬的题解心血来潮所以自己试着写了第一次题解。。可惜本蒟蒻的能力太差有两道题做不太出,只好搬运学习其它大佬的题解了。。

    a题

    https://vjudge.net/contest/355951#problem/A

    这题做题的过程十分痛苦

    我又双叒叕看错题意了。。

    以为是必须在对角线上

    其实是随便n*n的都行。。

    大概思路是从一个角开始更新,统计左边和上边相同的长度

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0);
    #define Mod 1000000007
    #define eps 1e-6
    #define ll long long
    #define INF 0x3f3f3f3f 
    #define MEM(x,y) memset(x,y,sizeof(x))
    #define Maxn 100005
    #define P pair<int,int>
    using namespace std;
    char a[1005][1005];
    int dp[1005][1005];
    int n;
    
    
    int main()
    {
        int n, ans;
        while (cin>>n , n)
        {
            ans = 1;
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                {
                    cin >> a[j][i];
                }
            for (int i = 0; i < n; i++)
            {
                for (int j = n - 1; j >= 0; j--)
                {
                    dp[i][j] = 1;
                    if (i == 0 || j == n - 1) continue;
                    int q = dp[i - 1][j + 1];
                    for (int k = 1; k <= q; k++)
                    {
                        if (a[i - k][j] == a[i][j + k]) dp[i][j]++;
                        else break;
                    }
                    ans = max(ans, dp[i][j]);
                }
            }
            printf("%d
    ", ans);
        }
        return 0;
    }

     b题

    https://vjudge.net/contest/355951#problem/B

    这题有点难。。

    蒟蒻直接下线。。。

    那就学下网上大佬的方法吧

    dp(j, k)表示,取j 个候选人,使其辩控差为k 的所有方案中,辩控和最大的那个方案(该方案称为“方案dp(j, k)”)的辩控和

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cmath>
    #include <vector>
    #include <algorithm>
    #include <string>
    #include <map>
    using namespace std;
    
    #define N 220
    #define met(a, b) memset(a, b, sizeof(a))
    #define INF 0xffffff
    const long long  Max = 2000000000;
    typedef long long LL;
    
    int D[N], P[N];
    int dp[30][1000];  ///dp[i][k]代表选 i 个人, 辩控差为 k 的辩控和最大
    int pre[30][1000]; ///pre[i][k] 存的是它是上次选的人
    int Answer[N];     ///记录选中的这 m 个人的编号
    
    int main()
    {
        int n, m, iCase=1;
    
        while(scanf("%d%d", &n, &m), n||m)
        {
            int i, j, k, Min, t1, t2;
    
            met(D, 0);
            met(P, 0);
            met(dp, -1);
            met(pre, 0);
            met(Answer, 0);
    
            for(i=1; i<=n; i++)
                scanf("%d%d", &D[i], &P[i]);
    
            Min = m*20; ///它的辩控差最大为 m*20  
            dp[0][Min] = 0;  ///起始状态要先置为 0
    
    
            /// k 的最大取值范围是[-Min, Min], 但是数组不能表示负数, 因此将数组向右平移 Min,得到[0, 2*Min]
            for(i=0; i<=m; i++)
            {
                for(k=0; k<=Min*2; k++)
                {
                    if(dp[i][k]==-1) continue;  ///如果存在,接着找 dp[i][k] 的下一个状态
    
                    for(j=1; j<=n; j++)
                    {
                            if(dp[i+1][k+D[j]-P[j]] < dp[i][k]+D[j]+P[j])
                            {
                                t1=i, t2=k;
    
                                while(t1>0 && pre[t1][t2]!=j)  
                                {
                                    t2 -= D[pre[t1][t2]] - P[pre[t1][t2]];
                                    t1 --;
                                }
                                if(t1==0)   ///当 t1 为 0 时,编号为 j 这个人在之前没有被选中过
                                {
                                    dp[i+1][k+D[j]-P[j]] = dp[i][k] + D[j] + P[j];
                                    pre[i+1][k+D[j]-P[j]] = j;
                                }
                            }
    
                    }
                }
            }
    
            int ff = Min;
            int num = 0, sum1=0, sum2=0;
    
            ///要选辩控差最小的,所求的 num 便是选 m 个人辩控差最小的 
            while(dp[m][ff-num]==-1 && dp[m][ff+num]==-1) num++;
    
            if(dp[m][ff-num]>dp[m][ff+num]) t2 = ff-num;
            else    t2 = ff+num;
    
            t1 = m;
    
            for(i=1; i<=m; i++)
            {
                Answer[i] = pre[t1][t2];
    
                sum1 += D[Answer[i]];
                sum2 += P[Answer[i]];
    
                t1--;
                t2 -= D[Answer[i]] - P[Answer[i]];
            }
    
            printf("Jury #%d
    ", iCase++);
            printf("Best jury has value %d for prosecution and value %d for defence:
    ", sum1, sum2);
    
    
            sort(Answer+1, Answer+1+m);
    
            for(i=1; i<=m; i++)
                printf(" %d", Answer[i]);
    
            printf("
    
    ");
        }
        return 0;
    }

      c题

      https://vjudge.net/contest/355951#problem/C

    总算有道水题,求子序列,前两天刚做过

    大体优化的思路就是用upper踢掉原来的数

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0);
    #define Mod 1000000007
    #define eps 1e-6
    #define ll long long
    #define INF 0x3f3f3f3f 
    #define MEM(x,y) memset(x,y,sizeof(x))
    #define Maxn 100005
    #define P pair<int,int>
    using namespace std;
    struct node {
        int l, w;
    }a[5005];
    int dp[5005];
    bool cmp(node a, node b)
    {
        if (a.w == b.w)
            return a.l < b.l;
        else
            return a.w < b.w;
    }
    int main()
    {
        int n, t;
        scanf("%d", &t);
        while (t--)
        {
            cin >> n;
            int i;
            for (i = 0; i < n; i++)
                scanf("%d%d", &a[i].l, &a[i].w);
            MEM(dp, INF);
            sort(a, a + n, cmp);
            for (i = n - 1; i >= 0; i--)
            dp[    lower_bound(dp, dp + n, a[i].l)-dp] = a[i].l;
            printf("%d
    ", lower_bound(dp, dp + n, INF) - dp);
        }
    }

    d题

    https://vjudge.net/contest/355951#problem/D

    还是子序列,事实告诉我们不能被英语打败

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0);
    #define Mod 1000000007
    #define eps 1e-6
    #define ll long long
    #define INF 0x3f3f3f3f 
    #define MEM(x,y) memset(x,y,sizeof(x))
    #define Maxn 100005
    #define P pair<int,int>
    using namespace std;
    int n;
    int a[500005];
    int s[500005];
    int main()
    {
        int t;
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d", &n);
            for(int i=1; i<=n; i++)
                scanf("%d", &a[i]);
            s[0]=0;
            int ans=0;
            for(int i=1; i<=n; i++)
            {
                if(a[i]>s[ans])
                {
                    s[++ans]=a[i];
                    continue;
                }
                int q=upper_bound(s, s+ans, a[i])-s;
                s[q]=a[i];
            }
            printf("%d
    ", ans);
        }
        return 0;
    }

    e题

    https://vjudge.net/contest/355951#problem/E

    2个小时的找bug时间竟然是因为一个大括号。。。

    这题的思路是开个二维的dp数组

    一边记录从左侧下的最优方案,另一个记录右侧的

    然后从底层往上慢慢推就行

    重要的是经验:对于这种只有两种选择的题可以尝试分别记录

    然后注意细节!!!

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0);
    #define Mod 1000000007
    #define eps 1e-6
    #define ll long long
    #define INF 0x3f3f3f3f 
    #define MEM(x,y) memset(x,y,sizeof(x))
    #define Maxn 100005
    #define P pair<int,int>
    using namespace std;
    struct node
    {
        int x;
        int y;
        int high;
         bool operator < (const node &other)const{
            return high < other.high;
        }
    };
    node p[1005];
    int dp[1005][2];
    int x, y, n, m;
    int qw;
    void left(int i)
    {
        int k = i - 1;
        while (k > 0 && p[i].high - p[k].high <= m)
        {
            if (p[i].x >= p[k].x && p[i].x <= p[k].y) {
                dp[i][0] = p[i].high - p[k].high + min(p[i].x - p[k].x + dp[k][0], p[k].y - p[i].x + dp[k][1]);
                return;
            }
            else
                k--;
        }
        if (p[i].high - p[k].high > m)
            dp[i][0] = INF;
        else
            dp[i][0] = p[i].high;
    }
    void right(int i)
    {
        int k = i - 1;
        while (k > 0 && p[i].high - p[k].high <= m)
        {
            if (p[i].y >= p[k].x && p[i].y <= p[k].y) {
                dp[i][1] = p[i].high - p[k].high + min(p[i].y - p[k].x + dp[k][0], p[k].y - p[i].y + dp[k][1]);
                return;
            }
            else
                k--;
        }
        if (p[i].high - p[k].high > m)
            dp[i][1] = INF;
        else
            dp[i][1] = p[i].high;
    }
    int main()
    {
        int t;
        cin >>t;
        while (t--)
        {
            cin >> n >> x >> y >> m;
            for (int i = 1; i <= n; i++)
                cin >> p[i].x >> p[i].y >> p[i].high;
        
        p[0].high = 0;
            p[0].x = -20000;
            p[0].y = 20000;
            p[n + 1].high = y;
            p[n + 1].x = p[n + 1].y = x;
        
            sort(p, p + n + 2);
            for (int i = 1; i <= n + 1; i++) {
                left(i);
                right(i);
            }
            int ans = min(dp[n + 1][0], dp[n + 1][1]);
            printf("%d
    ", ans);
        }
    }

    f题

    https://vjudge.net/contest/355951#problem/F

    一眼01背包

    但是好像又不太一样

    这道题存在负数还是两个参数怎么办

    负数------坐标横移

    两个参数-----把其中的一个作为dp数组的下标

    一发过了是我没想到的。。这不是我的风格

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0);
    #define Mod 1000000007
    #define eps 1e-6
    #define ll long long
    #define INF 0x3f3f3f3f 
    #define MEM(x,y) memset(x,y,sizeof(x))
    #define Maxn 100005
    #define P pair<int,int>
    using namespace std;
    int s[1005];
    int f[1005];
    int dp[200050];
    int main()
    {
        int n;
        while (cin >> n)
        {
            MEM(dp, -INF);
            for (int i = 0; i < n; i++)
            {
                cin >> s[i] >> f[i];
                
            }
            dp[100000] = 0;
            for (int i = 0; i < n; i++)
            {
                if (s[i] < 0 && f[i] < 0)
                    continue;
                if (s[i] > 0)
                {
                    for (int j = 200000; j > s[i]; j--)
                    {
                        if (dp[j - s[i]] > -INF)
                            dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
                    }
                }
                else
                {
                    for (int j = s[i]; j < 200000 + s[i]; j++)
                        if (dp[j - s[i]] > -INF)
                            dp[j] = max(dp[j], dp[j - s[i]] + f[i]);
                }
            }
            int ans = -INF;
            for (int i = 100000; i <= 200000; i++)
            {
                if (dp[i] >= 0)
                    ans = max(ans, dp[i] + i - 100000);
            }
            printf("%d
    ", ans);
    
            
        }
    }

    G题

    https://vjudge.net/contest/355951#problem/G

    挺简单的题但是还是卡了半天

    问题在于我一开始想把dp[i]定义为用前i个牛能达到的最高高度,但是没写出来

    然后我定义dp[i]类似为一个bool数组记录该高度是否能达到

    希望有大佬看到这里能私聊我讨论讨论第一种思路的可行性

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0);
    #define Mod 1000000007
    #define eps 1e-6
    #define ll long long
    #define INF 0x3f3f3f3f 
    #define MEM(x,y) memset(x,y,sizeof(x))
    #define Maxn 100005
    #define P pair<int,int>
    using namespace std;
    struct node
    {
        int h, a, c;
    }p[405];
    bool cmp(node x, node y)
    {
        return x.a < y.a;
    }
    int main()
    {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++)
        {
            cin >> p[i].h >> p[i].a >> p[i].c;
        }
        sort(p, p + n, cmp);
        int dp[1000000];
        int sum[100000];
        MEM(dp, 0);
        dp[0] = 1;
        int ans = 0;
        for (int i = 0; i < n; i++) {
            MEM(sum, 0);
            for (int j = p[i].h; j <= p[i].a; j++)
            {
                if (!dp[j] && dp[j - p[i].h] && sum[j-p[i].h] < p[i].c)
                {
                    sum[j] = sum[j - p[i].h] + 1;
                    dp[j] = 1;
                    ans = max(j, ans);
                }
            }
            
        }
        cout << ans;
    }

    H题

    https://vjudge.net/contest/355951#problem/H

    数学题!

    我数学好差。。公式好难推

    推出来也超时。。

    不过这题除了推公式没啥别的了。

    不会写数学符号直接上代码了

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <string>
    #include <stdlib.h>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <stack>
    #include <map>
    #include <set>
    using namespace std;
    #define ms(x, n) memset(x,n,sizeof(x));
    typedef  long long LL;
    const int inf = 1<<30;
    const LL maxn = 1e5+10;
    const int mod = 1e6;
    
    int N, M, l, r, a[1010];
    int dp[1010][maxn]; 
    int main()
    {
        int input;
        cin >> N >> M >> l >> r;
        for(int i = 1; i <= M; ++i){
            cin >> input;
            ++a[input];
        }
    
      
        for(int i = 0; i <= N; ++i)
            dp[i][0] = 1;
    
        for(int i = 1; i <= N; ++i){
            for(int j = 1; j <= M; ++j){
                if(j-a[i]-1 >= 0) 
                    dp[i][j] = (dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1-a[i]]+mod)%mod;
                else              
                    dp[i][j] = (dp[i][j-1]+dp[i-1][j]+mod)%mod;
            }
        }
        int ans = 0;
        for(int i = l; i <= r; ++i)
            ans = (ans+dp[N][i]+mod)%mod;
        cout << ans << endl;
    
        return 0;
    }

    I题

    https://vjudge.net/contest/355951#problem/I

    水题没啥说的

    啥?你问我为啥水题还re?

    ----N,M不分

    ----啊,那没事了

    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <queue>
    #include <string>
    #include <cstring>
    #include <map>
    #include <stack>
    #include <set>
    #include <sstream>
    #define IOS ios_base::sync_with_stdio(0); cin.tie(0);
    #define Mod 1000000007
    #define eps 1e-6
    #define ll long long
    #define INF 0x3f3f3f3f 
    #define MEM(x,y) memset(x,y,sizeof(x))
    #define Maxn 100005
    #define P pair<int,int>
    using namespace std;
    struct node
    {
        int s;
        int e;
        int v;
    }q[1005];
    bool cmp(node x, node y)
    {
        return x.e < y.e;
    }
    int main()
    {
        int n, m, r;
        int d;
        while(cin >> n >> m >> r)
        {
        
        for (int i = 0; i < m; i++)
        {
            cin >> q[i].s >> q[i].e >> q[i].v;
        }
        ll dp[10005];
        sort(q, q + m, cmp);
        for (int j = 0; j <m; j++) {
    
            dp[j] = q[j].v;
            for (int i = 0; i < j; i++)
            {
                if (q[j].s >= q[i].e + r)
                {
                    dp[j] = max(dp[j], dp[i] + q[j].v);
                }
            }
        }
        ll max = 0;
        for (int i = 0; i < m; i++)
        {
            if (dp[i] > max)
                max = dp[i];
        }
        cout << max<<endl;
    }
    }

    J题

    https://vjudge.net/contest/355951#problem/J

    自己写的代码三重循环tle了

    看一下数据不tle才怪。。

    学下网上大佬的思路吧

    f[i][j]表示到第i段路满足不下降(只做不下降,不上升待会再做一遍),且当前高度为j所需要的最小花费。它的转移实际上是mink[1,j]f[i1][k]+|h[i]ori[j]|mink∈[1,j]f[i−1][k]+|h[i]−ori[j]|,ori[j]是j对应的离散化之前的数,h[i]是i原来的高度,因为不下降,所以只能从[1,j][1,j]转移。不过这样的复杂度是O(n3)O(n3)的,我们发现,随着jj的增大,kk的取值范围只是上界变大了,因此可以通过更新前缀和来维护minn[i]=minj=[1,i]{f[j]}minn[i]=minj=[1,i]{f[j]}。

    真高级。。

    来人,上代码!

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    long long Abs(long long x)
    {
        return x>0?x:(-x);
    }
    long long Min(long long x,long long y)
    {
        return x<y?x:y;
    }
    struct node
    {
        long long a,b,i;
        friend bool operator <(node x,node y)
        {
            return x.i<y.i;
        }
    }a[2001];
    bool cmp(node x,node y)
    {
        return x.a<y.a;
    }
    long long ori[2001];
    long long f[2001];//f[i]代表f[i]到f[n]的最大值
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i].a);
            a[i].i=i;
        }
     
        std::sort(a+1,a+n+1,cmp);
        int cnt=0;
        a[0].a=-1;
        for(int i=1;i<=n;++i)
        {
            if(a[i].a!=a[i-1].a)
            {
                ++cnt;
                ori[cnt]=a[i].a;
            }
            a[i].b=cnt;
        }
        std::sort(a+1,a+1+n);
     
     
        long long ans=1000000000000000ll;
     
        memset(f,0,sizeof(f));
        f[cnt+1]=10000000000000ll;
        for(int i=n;i;--i)
        {
            for(int j=1;j<=cnt;++j)
                f[j]+=Abs(a[i].a-ori[j]);
            for(int j=cnt;j;--j)
                f[j]=Min(f[j],f[j+1]);
        }
        for(int i=1;i<=cnt;++i)
            ans=ans<f[i]?ans:f[i];
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    关于网页代码加密解密保护,保障页面安全
    DS--知识积累
    知识积累
    Nested DollsHDU1677
    CF335B
    HDU2385Stock
    滚动数组处理数据很大的公共子序列问题
    HDU4635
    HDU4638
    HDU4639
  • 原文地址:https://www.cnblogs.com/cyq123/p/12296640.html
Copyright © 2020-2023  润新知