• [USACO16DEC]Team Building P


    写出了$O((n+m)k^2)$的算法,但是却看不懂$O(nmk)$的算法。

    首先说$O((n+m)k^2)$的算法。

    把所有牛放在一起从大到小排序,相同则$A$的牛放在前面,问题变为从序列中选择一些元素,使得任意前缀$A$入选的个数大于$B$入选的个数。

    $f[i][j][k]$表示考虑了前$i$头牛,$A$入选了$j$头,$B$比$A$少$k$头,转移就判断下一头牛是$A$的还是$B$的,如果是$B$还要判断当前状态$k$是不是等于$0$。

    具体看代码吧。

    #include <bits/stdc++.h>
    #define Mid ((l + r) >> 1)
    #define lson (rt << 1)
    #define rson (rt << 1 | 1)
    using namespace std;
    const int mod = 1e9 + 9;
    int read(){
        char c; int num, f = 1;
        while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
        while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
        return f * num;
    }
    struct node {
        int x, t;
    } a[2009];
    int n, m, p, f[2009][2009][20];
    int cmp(node a, node b) {
        return (a.x == b.x && a.t > b.t) || a.x > b.x;
    }
    void up(int &a, int b) {a = (a + b) % mod;}
    signed main()
    {
        n = read(); m = read(); p = read();
        for(int i = 1; i <= n; i++) a[i] = (node){read(), 1};
        for(int i = 1; i <= m; i++) a[i + n] = (node){read(), 2};
        sort(a + 1, a + 1 + n + m, cmp); f[0][0][0] = 1;
        for(int i = 0; i <= n + m; i++) {
            for(int j = 0; j <= min(i, p); j++) {
                for(int k = 0; k <= j; k++) {
                    up(f[i + 1][j][k], f[i][j][k]);
                    if(a[i + 1].t == 1) up(f[i + 1][j + 1][k + 1], f[i][j][k]);
                    if(a[i + 1].t == 2 && k) up(f[i + 1][j][k - 1], f[i][j][k]);
                }
            }
        }
        printf("%lld
    ", f[n + m][p][0]);
        return 0;
    }
    View Code

    然后看到luogu题解里面一个$O(nmk)$的算法。

    他的递推是这样的

    for (int i=1;i<=p;i++){
        for (int j=1;j<=n;j++) for (int k=1;k<=m;k++) if (a[j]>b[k]) f[i][j][k]=f[i-1][j-1][k-1];
        for (int j=1;j<=n;j++) for (int k=1;k<=m;k++) f[i][j][k]=(f[i][j][k]+f[i][j][k-1])%mo;
        for (int j=1;j<=n;j++) for (int k=1;k<=m;k++) f[i][j][k]=(f[i][j][k]+f[i][j-1][k])%mo;
    }

    $f[i][j][k]$表示匹配了$i$对,A仅使用前$j$只,B仅使用前$k$只的方案数。

    一开始我想的这个复杂度算法递推是$f[i][j][k]=f[i-1][j][k]+f[i][j - 1][k]$以为是和$LCS$一样的处理方法,然而不行,因为会有方案重复,但是它的代码却对了,于是考虑为什么。

    发现他第一句计算出来后$f[i][j][k]$代表的意义是:匹配了$i$对,A第$j$只必选,B第$k$只必选的方案数。

    然后第二句,它固定了$j$相当于对$j$求了一个前缀和,代表的意义是:匹配了$i$对,A第$j$只必选,B考虑前$k$只的方案数。
    然后第三句,它固定了$k$相当于对$j$求了一个前缀和,代表的意义是:匹配了$i$对,A考虑前$j$只,B考虑前$k$只的方案数。

    心里只有一句妙啊。

  • 相关阅读:
    【NOIp模拟赛】种花
    【NOIP模拟赛】质数序列
    【NOIp模拟赛】兔子
    【NOIp模拟赛】圆桌游戏
    【NOIp模拟赛】花
    【洛谷P2345】奶牛集会
    【洛谷P1774】最接近神的人_NOI导刊2010提高(02)
    【洛谷P1495】 曹冲养猪
    【洛谷P1287】 盒子与球
    NOIP2009 Hankson 的趣味题
  • 原文地址:https://www.cnblogs.com/onglublog/p/14359997.html
Copyright © 2020-2023  润新知