Cloakroom BZOJ2794 POI2012(背包问题)

有n件物品,每件物品有三个属性a[i], b[i], c[i] (a[i] 再给出q个询问,每个询问由非负整数m, k, s组成,问是否能够选出某些物品使得:

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

a , b , m , s < = 1 e 9 a,b,m,s <=1e9 a,b,m,s<=1e9
n , c < = 1 e 3 n,c<=1e3 n,c<=1e3
k < = 1 e 5 k<=1e5 k<=1e5
q < = 1 e 6 q<=1e6 q<=1e6

题解:
首先这个题的 a , b , q a,b,q a,b,q都很大,这启发我们绝对不能存这些东西(这些东西不是突破口)。
发现 k k k可以接受,我们做一个大小为 1 e 5 1e5 1e5的背包,做 n ( 1 e 3 ) n(1e3) n(1e3)次。
好卡啊
那么怎么满足 a , b a,b a,b的限制呢?
可以离线把询问从小到大排序,物品按 a a a从小到大排序。
那么可以在边插入的时候直接对整个背包中求 b b b满足条件的答案。
我们就把 a a a给解决了。
那么 b b b呢?
发现合法的方案,所有的 b > m + s b>m+s b>m+s,也就是最小的 b > m + s b>m+s b>m+s,那么背包的时候我们 f [ x ] f[x] f[x]表示大小为 x x x的所有方案中最小的 b b b的最大值,那么 f [ k ] > m + s f[k] > m+s f[k]>m+s就和有方案达成条件等价。

A C   C o d e {AC\ Code} AC Code

#pragma GCC optimize(2)
#include
#define maxn 1005
#define pb push_back
using namespace std;

int n,q,c[maxn],a[maxn],b[maxn],d[maxn],f[100005],ans[1000005];
vector<int>l[maxn],K[maxn],id[maxn];
bool cmp(const int &u,const int &v){
	return a[u] < a[v];
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&c[i],&a[i],&b[i]);
		d[i] = i;
	}
	sort(d+1,d+1+n,cmp);
	scanf("%d",&q);
	for(int i=1,m,k,s;i<=q;i++){
		scanf("%d%d%d",&m,&k,&s);
		a[0] = m;
		int t = upper_bound(d+1,d+1+n,0,cmp)-d-1;
		l[t].pb(m+s),K[t].pb(k),id[t].pb(i);
	}
	memset(f,-0x3f,sizeof f);
	f[0] = 0x3f3f3f3f;
	for(int i=1;i<=n;i++){
		int u = d[i];
		for(int j=100000;j>=c[u];j--)
			f[j] = max(f[j] , min(f[j-c[u]],b[u]));
		for(int j=0;j<l[i].size();j++)
			ans[id[i][j]] = (f[K[i][j]] > l[i][j]);
	}
	for(int i=1;i<=q;i++)
		puts(ans[i]?"TAK":"NIE");
}

你可能感兴趣的:(DP,性质分析,DP状态及顺序)