1. 程式人生 > 實用技巧 >dp(揹包)

dp(揹包)

Cloakroom

題目描述

  • 有n件物品,每件物品有三個屬性a[i],b[i],c[i](a[i]<b[i])
  • q個詢問,每個詢問由非負整數m,k,s組成,問是否能夠選出某些物品使得:
  1. 對於每個選的物品i,滿足a[i]<=m且b[i]>m+s
  2. 所有選出物品的c[i]和正好是k

輸入格式

  • 第一行一個正整數n(n<=1000),接下來n行每行三個正整數,分別表示c[i],a[i],b[i](c[i]<=1000,1<=a[i]<b[i]<=1,000,000,000)
  • 下面一行一個正整數q(q<=1,000,000),接下來q行每行三個非負整數m,k,s(1<=m<=1,000,000,000,0<=k<=1,000,000,0<=s<=1,000,000,000)

輸出格式

輸出q行,每行為 " TAK" (yes )或"NIE " (no),第i行對應第i次詢問的答案。

樣例輸入

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

樣例輸出

TAK
NIE
TAK
TAK
NIE

思路:首先:若只有第二種限制,則是一個裸的揹包,所以,我們可以在01揹包的基礎上限制第一個條件
第二:限制a[i]<=m,將a[i]升序排列,將m也升序排列(所以需要離線操作,注意記錄每個詢問對應的序號,以便輸出)
   第三:限制b[i]>m+s,將f[i]定義為裝滿容量為i的揹包所有方案中每個方案最小b[i]的最大值(每個方案選最小b[i],確保方案中所有的b[i]都滿足b[i]>m+s,因為一種方案中最小的b[i]都比m+s大,其他肯定也比m+s大
選最大值是因為每個容量只需一種方案滿足即可,所以應選所有方案中最小b[i]的最大值更容易符合要求)
 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn=1000+10,inf=0x3f3f3f3f;
 5 int n,mm;
 6 int f[1000000+10];
 7 struct Node{
 8     int a,b,c;
 9     bool operator <(const Node &A)const{
10         return
a<A.a; 11 } 12 }e[maxn]; 13 struct Node1{ 14 int m,k,s,id; 15 bool operator <(const Node1 &a)const{ 16 return m<a.m; 17 } 18 }q[1000000+10]; 19 int ans[1000000+10]; 20 void solve(){ 21 int j=1; 22 f[0]=inf; 23 for(int i=1;i<=mm;i++){ 24 int m=q[i].m,k=q[i].k,s=q[i].s; 25 while(j<=n&&e[j].a<=m){//限制a[i]<=m,若e[j]不符合要求,跳出迴圈,當迴圈到下一個i時以前確定的f[i]值不變 26 for(int w=100000;w>=e[j].c;w--){ 27 f[w]=max(f[w],min(f[w-e[j].c],e[j].b)); 28 }//01揹包 29 j++; 30 } 31 if(f[k]>m+s) ans[q[i].id]=1;//若符合,則為真,注意ans的下標為查詢的序號而不是排序之後的 32 } 33 for(int i=1;i<=mm;i++){ 34 if(ans[i]) printf("TAK\n"); 35 else printf("NIE\n"); 36 } 37 } 38 int main(){ 39 scanf("%d",&n); 40 for(int i=1;i<=n;i++){ 41 scanf("%d%d%d",&e[i].c,&e[i].a,&e[i].b); 42 } 43 sort(e+1,e+1+n);//對a排升序 44 scanf("%d",&mm); 45 for(int i=1;i<=mm;i++){ 46 scanf("%d%d%d",&q[i].m,&q[i].k,&q[i].s); 47 q[i].id=i; 48 } 49 sort(q+1,q+1+mm);//對m排升序 50 solve(); 51 return 0; 52 }
View Code