1. 程式人生 > >10月20日備戰Noip2018模擬賽10 T2 Solve 屠題

10月20日備戰Noip2018模擬賽10 T2 Solve 屠題

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; 
}