Problem
一份產品有很多屬性,把每一個屬性當作一個維度,當成 $D$ 個維度的向量,當維度越多時絕對支配 (各方面都是最好的) 的機率就越低。若把 $D = 2$,就是在二維空間中的支配點 (Zerojudge d555 平面上的極大點)。由於絕對優勢的產品隨著維度增加而變少,定義出一種相對優勢產品,給定一個 $K$,若 $p_i$ 支配 (k-dominate) $p_j$,必須滿足下列三條:
- $\exists S' \subseteq S, |S'| = K$
 
- $\forall s_r \in S', p_i.s_r \geq p_j.s_r$
 
- $\exists s_t \in S', p_i.s_t > p_j.s_t$
 
用中文解釋這幾條數學公式,假設有 6 個維度的產品,目標 $K = 5$,則表示要在 6 的維度中任意挑選 5 個維度比較,若存在一種挑法可以支配,則可以說 $p_i$ k-dominate $p_j$
例如現在有 8 台筆記型電腦,共有 6 種屬性可以比較,數值越大越好。
| Notebook | 
CPU | 
RAM | 
HDD | 
HDD S. | 
Q. | 
VRAM | 
| $N_1$ | 
3 | 
3 | 
5 | 
6 | 
6 | 
8 | 
| $N_2$ | 
9 | 
4 | 
9 | 
7 | 
7 | 
9 | 
| $N_3$ | 
8 | 
4 | 
7 | 
9 | 
2 | 
7 | 
| $N_4$ | 
5 | 
6 | 
8 | 
9 | 
5 | 
9 | 
| $N_5$ | 
9 | 
7 | 
9 | 
6 | 
2 | 
4 | 
| $N_6$ | 
6 | 
6 | 
6 | 
5 | 
3 | 
5 | 
| $N_7$ | 
5 | 
7 | 
3 | 
8 | 
4 | 
6 | 
| $N_8$ | 
4 | 
4 | 
8 | 
6 | 
6 | 
4 | 
若要判斷 $N_2$ 是否支配 $N_3$,從中挑取 (CPU, RAM, HDD, HDD S., Q.) 這 5 個屬性比較,得到 $N_2$ 勝過於 $N_3$。最後可以發現到 $N_2$ 這產品可以支配 $N_1, N_3, N_5, N_6, N_8$。
現在問題來了,要找到不被任何產品支配的產品 (k-dominant skyline)。如上表 8 項產品的情況,只會有 $N_2, N_4$。
第一行會有一個整數 $T$ 表示有多少組測資。
每一組測資第一行會有三個整數 $N, D, K$,分別表示產品數量、產品屬性種類、以及支配比較的屬性種類。接下來會有 $N$ 行數據,每一行上會有 $D$ 個整數 $s_j$ 表示一個產品的屬性,產品按照輸入順序編號。
- $N < 1024$
 
- $0 < D, K < 8$
 
- $0 < s_j < 1000$
 
Output
對於每組測資輸出一行,輸出該組測資所有的 k-dominant skyline 的產品編號,由小到大輸出。若不存在則輸出 NONE。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 
  | 4 2 3 1 20 20 20 20 20 20 2 5 1 20 5 20 20 20 5 20 20 20 20 4 6 5 9 4 9 7 7 9 9 7 9 6 5 4 9 6 9 8 3 3 1 2 3 9 2 2 8 6 5 3 3 5 6 6 8 9 4 9 7 7 9 8 4 7 9 2 7 5 6 8 9 5 9 9 7 9 6 2 4 6 6 6 5 3 5 5 7 3 8 4 6 4 4 8 6 6 4 
  | 
 
Sample Output
1 2 3 4 
  | Case #1: 1 2 Case #2: NONE Case #3: 1 Case #4: 2 4 
  | 
 
Solution
這是一篇論文題目 k-dominant skyline set,網路上提出不少算法,看起來都是有容錯率。 
這一題只是介紹題,沒有強求高效算法計算,直接 $O(N^2)$ 的比較,稍微優化一下就可以。若要快一點,按照總和由大排到小,從概念上總和越大,表示支配別人的機率越高,那麼根據這種貪心思路去篩選,可以減少很多不必要的比較,速度會更快些。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 
  | #include <stdio.h> #include <stdlib.h> #include <set> #include <queue> #include <string.h> #include <algorithm> using namespace std; const int MAXN = 1024; int N, D, K; vector< vector<int> > C; struct Product {     int v[8], id;     bool operator<(const Product &a) const {         return sum() > a.sum();     }     int sum() const {         int s = 0;         for (int i = 0; i < D; i++)             s += v[i];         return s;     }     int domain(Product &u) {         for (auto &x : C) {             int h1 = 1, h2 = 0;             for (auto &e : x) {                 h1 &= v[e] >= u.v[e];                 h2 |= v[e] >  u.v[e];             }             if (h1 && h2)   return 1;         }         return 0;     } } item[MAXN]; int used[MAXN]; int main() {     int testcase, cases = 0;     scanf("%d", &testcase);     while (testcase--) {         scanf("%d %d %d", &N, &D, &K);         for (int i = 0; i < N; i++) {             for (int j = 0; j < D; j++)                 scanf("%d", &item[i].v[j]);             item[i].id = i;         }         sort(item, item+N);                  C.clear();         for (int i = 0; i < (1<<D); i++) {             if (__builtin_popcount(i) == K) {                 vector<int> A;                 for (int j = 0; j < D; j++) {                     if ((i>>j)&1)                         A.push_back(j);                 }                 C.push_back(A);             }         }                  vector<Product> filter;         vector<int> ret;         for (int i = 0; i < N; i++)             used[i] = 0;         for (int i = 0; i < N; i++) {             if (used[i] == 0) {                 filter.push_back(item[i]);                 for (int j = i+1; j < N; j++) {                     if (used[j] == 0 && item[i].domain(item[j]))                         used[j] = 1;                 }             }         }         for (int i = 0; i < filter.size(); i++) {             int ok = 1;             for (int j = 0; j < N && ok; j++) {                 if (item[j].domain(filter[i]))                     ok = 0;             }             if (ok == 1)                 ret.push_back(filter[i].id);         }                  sort(ret.begin(), ret.end());         printf("Case #%d:", ++cases);         for (int i = 0; i < ret.size(); i++)             printf(" %d", ret[i]+1);         puts(ret.size() ? "" : " NONE");     }     return 0; } 
  |