BZOJ-2794___Cloakroomr —— 离线 + 背包

bzoj上这题已经没了,不知道poi上还有没有。。。。。。
题目链接:这里
Description
n n n件物品,每件物品有三个属性 a [ i ] a[i] a[i], b [ i ] b[i] b[i], c [ i ] c[i] c[i] ( a [ i ] a[i] a[i] < < < b [ i ] b[i] b[i])。
再给出 q q q个询问,每个询问由非负整数 m m m, k k k, s s s组成,问是否能够选出某些物品使得:

  1. 对于每个选的物品 i i i,满足 a [ i ] < = m a[i]<=m a[i]<=m b [ i ] > m + s b[i]>m+s b[i]>m+s
  2. 所有选出物品的 c [ i ] c[i] c[i]的和正好是 k k k

Input
第一行一个正整数 n ( n < = 1 , 000 ) n (n<=1,000) n(n<=1,000),接下来n行每行三个正整数,分别表示c [ i ] , a [ i ] , b [ i ] ( c [ i ] < = 1 , 000 , 1 < = a [ i ] < b [ i ] < = 1 0 9 ) [i], a[i], b[i] (c[i]<=1,000, 1<=a[i]<b[i]<=10^9) [i],a[i],b[i](c[i]<=1,000,1<=a[i]<b[i]<=109)
下面一行一个正整数 q ( q < = 1 , 000 , 000 ) q (q<=1,000,000) q(q<=1,000,000),接下来q行每行三个非负整数 m , k , s ( 1 < = m < = 1 0 9 , 1 < = k < = 100 , 000 , 0 < = s < = 1 0 9 ) m, k, s (1<=m<=10^9, 1<=k<=100,000, 0<=s<=10^9) m,k,s(1<=m<=109,1<=k<=100,000,0<=s<=109)

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

Sample Input
5
6 2 7
5 4 9
1 2 4
2 5 8
1 3 9
5
2 7 1
2 7 2
3 2 0
5 7 2
4 1 5

Sample Output
TAK
NIE
TAK
TAK
NIE

题目大意:

    题面介绍的很详细,这里不做过多解释。

解题思路:

    由 a [ i ] < = m a[i]<=m a[i]<=m可以想到离线来解决。设物品为 p [ i ] p[i] p[i]、问题为 q [ i ] q[i] q[i]。先给物品的 p [ i ] p[i] p[i] a a a的值从小到大排个序,再给问题的 q [ i ] q[i] q[i] m m m的值从小到大排序,对于每个问题判断的时候,要求 p [ j ] . a < = q [ i ] . m p[j].a<=q[i].m p[j].a<=q[i].m,即可由 O ( n 2 ) O(n^2) O(n2)优化到 O ( n ) O(n) O(n)…不懂离线的先去Baidu。

    这样 a a a的判断我们就解决了, c c c很明显是背包的总量,可以按 01 01 01背包的思路解决,问题的关键是 b b b怎么判断???

    根据一般的方案数,我们设一个 d p dp dp数组来记录是否可行,但这个数组仅仅只是用来记录状态,即只有 0 0 0 1 1 1两种状态,比较浪费空间,我们可以用 d p [ i ] dp[i] dp[i]记录物品 c c c的和为 i i i时,在所有方案中,使最小的 b b b值 最大 的那个值。最后用这个 d p [ i ] dp[i] dp[i]去比较,若满足则记录到答案里。

代码思路:

    离线的方法不做多叙述,这里着重来讲解一下在所有方案中,使最小的 b b b值 最大 的那个值的代码思路。。。
    即for(int w=mk; w>=p[j].c; w--) dp[w] = max(dp[w], min(dp[w-p[j].c], p[j].b));
    怎么去理解这个语句呢,这里分布来理解,首先将 d p [ 0 ] = 1 < < 30 dp[0]=1<<30 dp[0]=1<<30,在设置状态的时候同时设为最大,一般情况下我们判断状态时是用if(dp[w-p[j].c]) dp[w] = dp[w-p[j].c];

    在这里,当 d p [ w − p [ j ] . c ] dp[w-p[j].c] dp[wp[j].c] 0 0 0时,我们将它同 p [ j ] . b p[j].b p[j].b比较,若以前的方案中的 b b b更小,则将它同目前的 d p [ w ] dp[w] dp[w]比较,获得最小 b b b里的 b b b最大的情况。
    反之,若 d p [ w − p [ j ] . c ] dp[w-p[j].c] dp[wp[j].c] 0 0 0的同时,发现当前物品的 p [ j ] . b p[j].b p[j].b更小,则更新这个最小值。然后再将这个最小值与所有情况里的最小值 d p [ w ] dp[w] dp[w]比较,取最大值,则得到了在所有方案中,使最小的 b b b值 最大 的那个值!!!

    时间复杂度: O ( Q l o g Q + N ∗ 1 0 5 ) O(QlogQ+N*10^5) O(QlogQ+N105)

核心:对于背包思路不是很熟练的人来说,比较烧脑。重点在于背吧不仅能存价值和方案数,还能存其他值,像本题的最小 b b b值的最大情况。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, qu, mk;
int ans[1000005], dp[1000005];
struct P {
	int c, a, b;
} p[1005];

struct Q {
	int m, k, s, id;
} q[1000005];

bool cmp_1(P pp1, P pp2) {
	return pp1.a<pp2.a;
}

bool cmp_2(Q qq1, Q qq2) {
	return qq1.m<qq2.m;
}

int main() {
	scanf("%d", &n);
	for(int i=1; i<=n; i++)
		scanf("%d%d%d", &p[i].c, &p[i].a, &p[i].b);
	scanf("%d", &qu);
	for(int i=1; i<=qu; i++) {
		scanf("%d%d%d", &q[i].m, &q[i].k, &q[i].s);
		q[i].id = i; mk = max(mk, q[i].k);
	}
	sort(p+1, p+1+n, cmp_1);
	sort(q+1, q+1+qu, cmp_2);
	dp[0] = 1<<30;

	for(int i=1, j=1; i<=qu; i++) {
		while(j<=n && p[j].a<=q[i].m) {
			for(int w=mk; w>=p[j].c; w--)
				dp[w] = max(dp[w], min(dp[w-p[j].c], p[j].b));
			j++;
		}
		ans[q[i].id] = (q[i].m+q[i].s < dp[q[i].k]);
	}

	for(int i=1; i<=qu; i++) {
		if(ans[i]) puts("TAK");
		else puts("NIE");
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值