BZOJ2794 [Poi2012]Cloakroom [离线][DP]

BZOJ2794 [Poi2012]Cloakroom [离线][DP]

Description

有n件物品,每件物品有三个属性a[i], b[i], c[i] (a[i]

Input

第一行一个正整数n (n<=1,000),接下来n行每行三个正整数,分别表示c[i], a[i], b[i] (c[i]<=1,000, 1<=a[i]

Output

输出q行,每行为TAK (yes)或NIE (no),第i行对应第i此询问的答案。

题解

感觉波兰的题好多都是思维题…

选出的物品有两个要求,第二个要求只能用DP了,所以必须在第一个要求上优化

一开始考虑了一下离线,但是毫无思路

离线:

首先将每个物品按照 a [ i ] 的值从小到大排序,其次将询问按照 m 的值从小到大排序

这样就不用每次重新寻找 a [ i ] 的临界值,从 O ( n 2 ) 变为 O ( n ) ;而且此时只用寻找这些物品( c 值之和恰好为 k )中 b [ i ] 的最小值,只要这个最小值大于 m + s ,就TAK,否则NIE

然后背包DP:设定状态 f [ i ] 表示当物品的 c 值和为 i 时,这些物品中 b 的最小值

方程:见代码

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct node{int a,b,c;}Att[1100];
struct Node{int m,k,s,id;}Que[1000100];
bool operator<(const node &w,const node &e){
    return w.a<e.a;
}
bool operator<(const Node &w,const Node &e){
    return w.m<e.m;
}
int f[100100];bool Ans[1000100];
int main(){
    int n,Max=0;scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&Att[i].c,&Att[i].a,&Att[i].b);
    sort(Att+1,Att+n+1);
    int q;scanf("%d",&q);
    for(int i=1;i<=q;i++){
        scanf("%d%d%d",&Que[i].m,&Que[i].k,&Que[i].s);
        Max=max(Max,Que[i].k);Que[i].id=i;
    }
    sort(Que+1,Que+q+1);f[0]=1e9;
    for(int cnt=1,i=1;cnt<=q;cnt++){
        while(Att[i].a<=Que[cnt].m && i<=n){
            for(int t=Max;t>=Att[i].c;t--)
                f[t]=max(f[t],min(f[t-Att[i].c],Att[i].b));
            i++;
        }
        if(f[Que[cnt].k]>Que[cnt].m+Que[cnt].s)
            Ans[Que[cnt].id]=true;
    }
    for(int i=1;i<=q;i++)
        if(Ans[i])puts("TAK");else puts("NIE");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/arliastark/article/details/81074660