[POI 2012]Cloakroom(DP)

题目链接

http://main.edu.pl/en/archive/oi/19/sza

题目大意

有n件物品,每件物品有三个属性a[i], b[i], c[i] ( a[i]<b[i] )。
再给出q个询问,每个询问由非负整数m, k, s组成,问是否能够选出某些物品使得:
1. 对于每个选的物品i,满足a[i]<=m且b[i]>m+s。
2. 所有选出物品的c[i]的和正好是k。

思路

如果对于每次询问都从头开始做背包的话,那么肯定TLE。
考虑离线处理,一次背包回答所有的询问。对每个询问按照 m 升序排序,对每个物品按照 a 升序排序。然后开始做背包DP,维护当前要加入的物品 j ,依次枚举询问 i ,然后按照之前排序的顺序依次加入物品。用 f[i][j] 表示前 i 个物品,在满足 am 的前提下,凑出体积 k 的话,选出的物品里的 b 最小值最大是多少。这个DP和一般的背包DP非常相似,因此我就不需作过多赘述了。之前的DP方程也可以和一般的背包DP一样用滚动数组优化成一维空间。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 1100
#define MAXK 110000
#define MAXQ 1100000
#define INF 0x3f3f3f3f

using namespace std;

int n,q;

struct Thing
{
    int a,b,c;
}things[MAXN];

struct Query
{
    int m,k,s,id;
}query[MAXQ];

bool ans[MAXQ];

bool cmp_thing(Thing a,Thing b)
{
    return a.a<b.a;
}

bool cmp_query(Query a,Query b)
{
    return a.m<b.m;
}

int f[MAXK];

int main()
{
    int maxx=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&things[i].c,&things[i].a,&things[i].b);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
        query[i].id=i,scanf("%d%d%d",&query[i].m,&query[i].k,&query[i].s),maxx=max(maxx,query[i].k);
    sort(things+1,things+n+1,cmp_thing);
    sort(query+1,query+q+1,cmp_query);
    f[0]=INF;
    for(int i=1,j=1,sum=0;i<=q;i++) //回答第i个询问,当前决策是否放入第j个物品
    {
        for(;j<=n&&things[j].a<=query[i].m;sum+=things[j].c,j++)
            for(int k=min(maxx,sum);k>=0;k--)
                if(f[k])
                    f[k+things[j].c]=max(f[k+things[j].c],min(things[j].b,f[k]));
        ans[query[i].id]=f[query[i].k]>(query[i].m+query[i].s);
    }
    for(int i=1;i<=q;i++)
        printf("%s\n",ans[i]?"TAK":"NIE");
    return 0;
}

你可能感兴趣的:([POI 2012]Cloakroom(DP))