zoukankan      html  css  js  c++  java
  • hdu 4381(背包变形)

    题意:

    给定n个块,编号从1到n,以及m个操作,初始时n个块是白色。

    操作有2种形式:

    1 ai xi : 从[1,ai]选xi个块,将这些块涂白。

    2 ai xi:从[ai,n]选xi个块,将这些块涂白。

    可以忽略某些操作且如果区间内没有足够的黑块(黑块用于涂白),则不能进行这个操作。

    分析:

    写写画画一看就知道这道题是一个背包问题。

    “恰好装满背包”。

    以下摘自题解:

    本题难点在于正确处理两种操作,不妨假设只有一种操作,那么这种操作如果是1的话那么就把操作按照a从小到大排序,每次都尽量往最左边涂,如果是2的话则类似的涂到最右边,但本题两种操作都出现了。

    先考虑第一问:

    我们把所有的操作按类别区分开,假设所有的1操作尽量用上能从1涂到a格子,所有的2操作尽量用上能从b格子涂到n,假设a<b,那么答案显然是a+n-b+1。那么假设a>=b,那么假设1操作从1涂到x,那么2操作一定会从n尽量往左边涂,直到x为止。最后两边的总和就是答案。

    由上不难想到一个DP,l[i][j]表示用了前i种1操作,从1涂到j的最小操作数,转移l[i][j]=min(l[i][j],l[i-1][j-ope[i].x]+1)(ope[i].x<=j<=ope[i].a),类似的,我们可以得到r[i][j]表示用2操作从j涂到n需要的最小操作数。dp复杂度O(n*m)。(这个背包也可以压缩一维,后面的l,r均为1维的状态)

    那么我们倒着枚举涂色最大的数目,假设为k,这个数目必然由1操作贡献一部分,由2操作贡献一部分(也可能一个贡献全部,另一个为0),那么我们枚举1操作涂到了i,那么2操作必然涂到了n-(k-i)+1,如果l[i]和r[n-(k-i)+1]均有值,那么说明这个最大数目是可达的,即是第一问的答案。枚举复杂度O(n*n)。

    再考虑第二问:

    现在我们已经知道最大数目了,这个数目是由操作1和操作2一起贡献的,那么我们仍然可以枚举操作1涂到了i,那么操作2涂到了n-(ans-i)+1,如果l[i]和r[n-(k-i)+1]均有值,那么其和可以用来更新第二问的答案,最后第二问的答案为所有合法方案需操作数的最小值。枚举复杂度O(n*n)。

    代码实现:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <cmath>
    
    using namespace std;
    const int maxn = 2005;
    struct node{
        int aa,cor;
        node(){}
        node(int _aa,int _cor){
           aa = _aa;   cor = _cor;
        }
    }x1[maxn],x2[maxn];
    int n,m;
    int dp1[maxn],dp2[maxn];
    int sumx1,sumx2;
    
    bool cmp(const node &p,const node &q){
        return p.aa < q.aa;
    }
    
    int min(int a,int b)
    {
        return a<b?a:b;
    }
    
    int main()
    {
        int cas,ta=1;
        scanf("%d",&cas);
        while(cas--){
             int i,j;
             scanf("%d%d",&n,&m);
    
             sumx1 = sumx2 = 1;
             for(i=0; i<m; i++){
                 int key,yy,z;
                 scanf("%d%d%d",&key,&yy,&z);
                 if(key == 1){
                    x1[sumx1++] = node(yy,z);
                 }else{
                    x2[sumx2++] = node(n+1-yy,z);
                 }
             }
             sort(x1+1,x1+sumx1,cmp);
             sort(x2+1,x2+sumx2,cmp);
             memset(dp1,0x3f,sizeof(dp1));
             memset(dp2,0x3f,sizeof(dp2));
    
             dp1[0] = dp2[0] = 0;
             for(i=1; i<sumx1; i++){
                 for(j=x1[i].aa; j>=x1[i].cor; j--){
                    dp1[j] = min(dp1[j],dp1[j-x1[i].cor]+1);
                 }
             }
    
             for(i=1; i<sumx2; i++){
                 for(j=x2[i].aa; j>=x2[i].cor; j--){
                    dp2[j] = min(dp2[j],dp2[j-x2[i].cor]+1);
                 }
             }
    
             int ans = 0,anssum = 0,tmp;
             for(i=1; i<=n; i++){
                for(j=0; j<=i; j++){
                   tmp = dp1[j] + dp2[i-j];
                   if(tmp <= m){
                      if(ans != i){
                        ans = i;   anssum = tmp;
                      }else if(tmp < anssum){
                          anssum = tmp;
                      }
                   }
                }
             }
    
             printf("Case %d: %d %d
    ",ta++,ans,anssum);
        }
        return 0;
    }
  • 相关阅读:
    前端与算法 leetcode 344. 反转字符串
    JavaScript闭包使用姿势指南
    前端与算法 leetcode 48. 旋转图像
    前端与算法 leetcode 36. 有效的数独
    前端与算法 leetcode 1. 两数之和
    前端与算法 leetcode 283. 移动零
    前端与编译原理 用js去运行js代码 js2run
    前端与算法 leetcode 66. 加一
    前端与算法 leetcode 350. 两个数组的交集 II
    前端与算法 leetcode 26. 删除排序数组中的重复项
  • 原文地址:https://www.cnblogs.com/jiangjing/p/3855015.html
Copyright © 2011-2022 走看看