P3537 [POI2012]SZA-Cloakroom (揹包)
阿新 • • 發佈:2020-07-19
- 有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<=1,000),接下來 n 行每行三個正整數,分別表示 c[i],a[i],b[i] (c[i]<=1,000,1<=a[i]<b[i]<=109)。
- 下面一行一個正整數 q(q<=1,000,000),接下來 q 行每行三個非負整數 m,k,s(1<=m<=109,1<=k<=100,000,0<=s<=109)。
輸出格式
- 輸出 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
演算法分析
- 思維含量蠻高的一個 暴力很好想的
- 先來講講暴力的思想: 按照題目描述很容易我們會想到揹包問題 直接按照揹包問題的格式跑就好了 如果當前物品的a[i] > m || b[i] < m + s 那麼就讓當前狀態揹包繼承上一狀態,不然就可以試著轉移 選擇最優,每次按照k的容量跑揹包,如果最後最大價值是k 那麼就表示當前物品可以,裸的板子
- 但是再看複雜度 一個q的詢問就如此之大了 顯然不能直接跑揹包 所以應該怎麼做?
- 我們可以把線上詢問轉化為離線做法 然後就很容易做了
- 對於第一個條件a[i] <= m 我們可以把a[i] 和 m 都升序排列一下
- 關於第二個條件b[i] > m + s 我們可以用一個數組來維護組成數字之和為k的物品中最小的b屬性(如果它都滿足顯然其它的都滿足)
- 定義陣列f[i] 表示 c屬性之和為i的幾個物品(而且滿足a屬性)的b屬性的最小值
程式碼展示
#include<bits/stdc++.h> using namespace std; const int maxv = 1e6+10; int n,m,sum; int f[maxv * 10]; int ans[maxv * 10]; struct node{ int a,b,c; }a[maxv]; struct Node{ int m,k,s,id; }b[maxv * 10]; bool cmp(node a,node b){return a.a < b.a;} bool Cmp(Node a,Node b){return a.m < b.m;} int main(){ scanf("%d",&n); for(int i = 1;i <= n;++i)scanf("%d%d%d",&a[i].c,&a[i].a,&a[i].b); scanf("%d",&m); for(int i = 1;i <= m;++i){ scanf("%d%d%d",&b[i].m,&b[i].k,&b[i].s); b[i].id = i;//記錄詢問順序防止排序排亂 } sort(a + 1,a + n + 1,cmp); sort(b + 1,b + m + 1,Cmp); f[0] = 0x3f3f3f3f; int j = 1; for(int i = 1;i <= m;++i){ while(j <= n && a[j].a <= b[i].m){ for(int k = 100000;k >= a[j].c;--k) f[k] = max(f[k],min(f[k - a[j].c],a[j].b)); ++j; } if(f[b[i].k] > b[i].m + b[i].s)ans[b[i].id] = 1;//如果最小的都滿足 } for(int i = 1;i <= m;++i){ if(ans[i])printf("TAK\n"); else printf("NIE\n"); } }