10月20日備戰Noip2018模擬賽10 T2 Solve 屠題
阿新 • • 發佈:2018-12-16
10月20日備戰Noip2018模擬賽10
T2 Solve 屠題
題目描述
蒟蒻Hsq被佈置了n道作業題,可是他一道也不會..但他知道有w個czk大佬的分身,並知道每隻czk分身會做哪些題(雖然czk會做所有的題,但是為了題目需要要讓他變弱一點),請問Hsq至少請多少位czk的分身,才能屠完所有的題?
輸入格式
第一行兩個整數n,w表示有n道作業題和w位czk的分身,作業題以1..n編號.接下來w行,第i+1行第一個數li表示第i位分身會做的題目的數量,接下來li個數表示第i位czk分身會做哪些題目。
輸出格式
一個數,蒟蒻Hsq至少要請多少位czk的分身
輸入樣例
4 4 2 1 2 1 4 3 2 3 4 2 1 3
輸出樣例
2
資料範圍
對於40%的資料,3<=n,w<=10,
對於100%的資料,3<=n,w<=60,1<=li<=6
思路
搜尋 + 剪枝
1 可行性剪枝,如果當前選擇的高手的數量已經大於等於當前最優解的數量,剪.這也是最基礎,最簡單,但卻是最實用的剪枝之一.
2 重複資料 資料中不可避免地會出現某一個高手會做的題目,有另外一個高手全會做的情況.這種情況下,這個高手就不需要了,因為它完全可以被另外那個高手取代.
3 僅有情況 有的題只能被一位高手解決,所以在搜尋之前把這位高手會做的題目刪去吧,最優解中一定包含這位高手,所以這些題一定能被解決.
程式碼
#include <iostream> #include <cstdio> using namespace std; const int N = 61; const int INF = 0x7fffffff; int n, m, ans = INF, w; int a[N][N], l[N], who[N][N], ok[N], d[N]; bool b[N], can[N][N]; bool be_included(int x) { for (int i = 1; i < x; i ++){ bool f = 1; for (int j = 1; j <= l[x]; j ++){ if (!can[i][a[x][j]]){ f = 0; break; } } if (f) return true; } return false; } void dfs(int k, int s) { if (s >= ans) return; if (k > n){ ans = s; return; } if (ok[k] > 0) { dfs(k + 1, s); return; } int use; for (int i = 1; i <= d[k]; i ++){ use = who[k][i]; for (int j = 1; j <= l[use]; j ++) ok[a[use][j]]++; dfs(k + 1, s + 1); for (int j = 1; j <= l[use]; j ++) ok[a[use][j]]--; } } int main() { //freopen("solve.in", "r", stdin); //freopen("solve.out", "w", stdout); scanf("%d %d", & n, & m); for (int i = 1; i <= m; i ++){ scanf("%d", & l[i]); for (int j = 1; j <= l[i]; j ++){ scanf("%d", & a[i][j]); can[i][a[i][j]] = true; } } for (int i = 1; i < m; i ++){ for (int j = i + 1; j <= m; j ++){ if (l[i] < l[j]){ swap(l[i], l[j]); for (int k = 1; k <= 6; k ++) swap(a[i][k], a[j][k]); for (int k = 1; k <= 60; k ++) swap(can[i][k], can[j][k]); } } } for (int i = 1; i <= m; i ++){ if (!be_included(i)){ w++; l[w] = l[i]; int tot = 0; for (int j = 1; j <= n; j ++){ if (can[i][j]){ tot++; a[w][tot] = j; who[j][++d[j]] = w; } } } } int tot = 0; for (int i = 1; i <= n; ++i){ if (d[i] == 1 && ok[i] == 0){ tot++; int use = who[i][1]; for (int j = 1; j <= l[use]; ++j) ok[a[use][j]]++; } } dfs(1, tot); printf("%d", ans); //fclose(stdin); //fclose(stdout); return 0; }