BZOJ-2794___Cloakroomr——离线+背包

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

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

Input
第一行一个正整数 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)
下面一行一个正整数 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)

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

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

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

代码思路:

    离线的方法不做多叙述,这里着重来讲解一下在所有方案中,使最小的 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 ,在设置状态的时候同时设为最大,一般情况下我们判断状态时是用if(dp[w-p[j].c]) dp[w] = dp[w-p[j].c];

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

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

核心:对于背包思路不是很熟练的人来说,比较烧脑。重点在于背吧不仅能存价值和方案数,还能存其他值,像本题的最小 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; i<=qu; i++)
	{
		for(int j=1; j<=n && p[j].a<=q[i].m; j++)
		{
			for(int w=mk; w>=p[j].c; w--)
				dp[w] = max(dp[w], min(dp[w-p[j].c], p[j].b));
		}
		
		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");
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Scar_Halo/article/details/82913711