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\n",ta++,ans,anssum);
    }
    return 0;
}

posted on 2014-07-19 15:19  后端bug开发工程师  阅读(776)  评论(0编辑  收藏  举报

导航