bzoj上这题已经没了,不知道poi上还有没有。。。。。。
Description
有
件物品,每件物品有三个属性
,
,
(
)。
再给出q个询问,每个询问由非负整数
,
,
组成,问是否能够选出某些物品使得:
- 对于每个选的物品 ,满足 且 。
- 所有选出物品的 的和正好是 。
Input
第一行一个正整数
,接下来n行每行三个正整数,分别表示c
。
下面一行一个正整数
,接下来q行每行三个非负整数
。
Output
输出
行,每行为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
题目大意:
题面介绍的很详细,这里不做过多解释。
解题思路:
由 可以想到离线来解决。设物品为 、问题为 。先给物品的 按 的值从小到大排个序,再给问题的 按 的值从小到大排序,对于每个问题判断的时候,要求 ,即可由 优化到 …不懂离线的先去Baidu。
这样 的判断我们就解决了, 很明显是背包的总量,可以按 背包的思路解决,问题的关键是 怎么判断???
根据一般的方案数,我们设一个 数组来记录是否可行,但这个数组仅仅只是用来记录状态,即只有 和 两种状态,比较浪费空间,我们可以用 记录物品 的和为 时,在所有方案中,使最小的 值 最大 的那个值。最后用这个 去比较,若满足则记录到答案里。
代码思路:
离线的方法不做多叙述,这里着重来讲解一下在所有方案中,使最小的
值 最大 的那个值的代码思路。。。
即for(int w=mk; w>=p[j].c; w--) dp[w] = max(dp[w], min(dp[w-p[j].c], p[j].b));
怎么去理解这个语句呢,这里分布来理解,首先将
,在设置状态的时候同时设为最大,一般情况下我们判断状态时是用if(dp[w-p[j].c]) dp[w] = dp[w-p[j].c];
。
在这里,当
非
时,我们将它同
比较,若以前的方案中的
更小,则将它同目前的
比较,获得最小
里的
最大的情况。
反之,若
非
的同时,发现当前物品的
更小,则更新这个最小值。然后再将这个最小值与所有情况里的最小值
比较,取最大值,则得到了在所有方案中,使最小的
值 最大 的那个值!!!
时间复杂度:
核心:对于背包思路不是很熟练的人来说,比较烧脑。重点在于背吧不仅能存价值和方案数,还能存其他值,像本题的最小 值的最大情况。。。
#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;
}