24/9/10 kuangbin_dp

24_9_10 完成在kuangbin专题基础dp中的5道题

还是感觉太菜了

Max Sum Plus Plus

Hdu1024

题意:即求最大m子段和,最大子段和的进阶
思路:我们设置 \(dp[i][j]\) 为选择第j个数下分成i段的最大值,因此状态转移为 $dp[i][j] = \max(dp[i][j - 1],\max dp[i - 1][k]) (i-1 \leq k \leq n) $
情况1:第 \(j\) 个数单独作为一段,但是我们并不知道第 \(i - 1\) 段是以什么结尾,因此是 $ \max dp[i - 1][k]$,情况2:第 \(i\) 个数不单独作为一段,那么由于连续性一定是和 \(j - 1\) 同为一段,因此是 \(dp[i][j - 1]\)
需要注意的是:这里显然可以用滚动数组优化,同时在枚举第i段时应当将 \(dp[i - 1]\)\(ma[i - 1]\) 赋值为-INF,原因是 \(i - 1\) 个元素无法分成i段,对状态转移不应该有影响

void solve() {
    while(~scanf("%lld%lld",&m,&n)) {
        init();
        for(int i = 1;i <= n;i ++ )
            scanf("%lld",&a[i]);
        for(int i = 1;i <= m;i ++ ) {
            dp[i - 1] = -INF;   //因为分成i段 i - 1个元素显然不够
            for(int j = i;j <= n;j ++ ) {
                dp[j] = a[j] + std::max(dp[j - 1],ma[j - 1]);
            }
            ma[i - 1] = -INF;   //同上
            for(int j = i;j <= n;j ++ ) {
                ma[j] = std::max(ma[j - 1],dp[j]);
            }
        }
        int ans = -INF;
        for(int i = m;i <= n;i ++ )
            ans = std::max(ans,dp[i]);
        printf("%lld\n",ans);
    }
}   

Ignatius and the Princess IV

HDU1029

水题,用数组模拟同时注意数组大小就好

Super Jumping! Jumping! Jumping!

HDU1087

题意:在数组中每次只能走比当前更大的值,求最后的最大值
思路:设置 \(dp[i]\) 为选择第i个数的最大值,显然有 $dp[i] = \max dp[k] + a[i] 1 (\leq k \leq i - 1) $,类似最长不下降子序列的方式计算

void solve() {
    while((std::cin >> n) && n != 0) {
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        for(int i = 1;i <= n;i ++ )
            std::cin >> a[i];
        for(int i = 1;i <= n;i ++ ) {
            int t = -INF;
            for(int j = 1;j <= i - 1;j ++ ) {
                if(a[i] > a[j]) t = std::max(t,dp[j]);
            }
            dp[i] = a[i] + std::max(0,t);
        }
        int ans = -INF;
        for(int i = 1;i <= n;i ++ )
            ans = std::max(ans,dp[i]);
        std::cout << ans << "\n";
    }
}   

最少拦截系统

HDU1257

前置知识:Dilworth定理(https://oi-wiki.org/math/order-theory/),即在偏序集中最长链的长度等于最小的反链覆盖数,对偶形式偏序集中最长反链的长度等于最小的链
的覆盖长度(oiwiki中有对偏序列,反链的说明)
思路:即题目求的是不上升子序列的最小覆盖数,数列在关系 \(\leq\) 是满足自反性、传递性、反对称性,因此满足偏序集,因此问题等价为最长链的长度,即求最长上升子序列

void solve() {
    while(std::cin >> n) {
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        for(int i = 1;i <= n;i ++ )
            std::cin >> a[i],dp[i] = 1;
        for(int i = 1;i <= n;i ++ )
            for(int j = 1;j < i;j ++ ) {
                if(a[j] < a[i]) dp[i] = std::max(dp[i],dp[j] + 1);
            }
        int ans = -INF;
        for(int i = 1;i <= n;i ++ )
            ans = std::max(ans,dp[i]);
        std::cout << ans << "\n";
    }
}

免费馅饼

HDU1176

思路:是典型的数塔模型,即我们按时间高低进行排列,最后都是汇集在(0,5)这个点上,直接按数塔模型计算即可
注意:可以把所有的坐标往右移动一格,避免计算位置0和11需要特殊讨论

void solve() {
    while(std::cin >> n && n != 0) {
        // std::cin >> n;
        int t = -INF;
        memset(dp,0,sizeof(dp));
        for(int i = 1;i <= n;i ++ ) {
            int x,y;
            std::cin >> x >> y;
            dp[y][++ x] ++;
            t = std::max(t,y);
        }
        // std::cout << t << "\n";
        for(int i = t;i >= 0;i -- ) 
            for(int j = 1;j <= 11;j ++ ) {
                dp[i][j] += std::max({dp[i + 1][j - 1],dp[i + 1][j],dp[i + 1][j + 1]});
            }
        // printf("%d\n",dp[0][6]);
        std::cout << dp[0][6] << "\n";
    }
}
posted @ 2024-09-10 21:10  nnnnakosuki  阅读(25)  评论(0)    收藏  举报