Topic: Livelihood Issues

topic description

A city government is very concerned about people's livelihood. It has recently conducted a survey on people's livelihood issues and proposed n problems to be solved recently. The government's expert advisory group has w people, and each expert has its own specialty. The government knows that each expert can solve it For which problems, the government now wants to know how many experts it needs at least to solve all the problems?

input and output format

Input format:

The data is read from the file question.in. The two integers n and w in the first line indicate that there are n problems to be solved and w experts. The problems to be solved are numbered with 1...n. In the next w line, the first number li in line i+1 represents the number of problems that the i-th expert can solve, and the next li number represents the number of problems that the i-th expert can solve.

Output format:

Output the result to the file question.out, there is only one number, which indicates at least how many experts should be invited.

Input and output samples

Input example #1:

4 4
2 1 2
1 4
3 2 3 4
2 1 3

Sample output #1:

2

prompt information

Data range:
For 40% of the data, 3<=n, w <=10
For 100% of the data, 3 <=n, w <=60, 1<=li <=6

train of thought

The first thing I saw when I saw this question was search, but the data range...
But the correct solution to this question is search , but it just adds a lot of pruning

Let me talk about the pruning of search first, which is divided into feasibility pruning and optimal pruning .
Obviously, conventional optimal pruning can be used: if the number of currently selected experts > the current optimal value during the search, directly return.

Secondly, it is unconventional pruning:

1. If the problem that expert a can solve, expert b can also solve, then expert a can not be included in the search scope ( don’t ask me why )

2. If a problem can only be solved by expert a, then expert a must be selected and not included in the search scope

After talking about pruning, it is the question of how to search:
enumerate each (here refers to the experts within the search range) experts to choose or not, until all problems are solved or all experts have been traversed (this is what I did at the beginning)

Enumerate which expert to choose for each unsolved problem, and mark other problems that the expert can solve by the way (a bit awkward). Enumerate from the first problem to the last problem, and skip the solved problems at the same time

Obviously, the second type is more efficient.
Don’t ask me how I came up with these (the teacher said~)
In addition, this question is really troublesome, so you have to do it step by step, don’t rush

Probably the idea is like this, you can understand with the help of code and comments:

AC code

#include <iostream>
#include <cstdio>
using namespace std;
int n , m , sum , ans , sol[110];
//sol[i]:问题i是否被解决(注意int类型,往下看就知道了) 
bool map[110][110], out[110];
//map[i][j]:false:i专家不能解决j问题,true:能解决,out[i]:i专家是否在搜索范围以内(true表示已经被踢出(不在范围内))
int p[110][110] , pn[110];//p[i][j]:i问题能被j专家解决(链表) ,pn[i]为能解决i问题的专家数量 
int q[110][110] , qn[110];//q[i][j]:i专家能解决j问题(链表) ,qn[i]为i专家能解决的问题数量 
void dfs(int x , int nowsum){
	if(x == m + 1){//记录最优值 
		if(nowsum < ans)
			ans = nowsum;
		return;
	}
	if(nowsum >= ans)//最优性剪枝 
		return;
	
	for(int i = 1 ; i <= pn[x] ; i++){
		for(int j = 1 ; j <= qn[p[x][i]] ; j++)//枚举可以解决x这个问题的专家可以解决的所有问题 
			sol[q[p[x][i]][j]]++;//可以解决x这个问题的专家可以解决的问题的专家+1 
			//这里注意:sol[i]表示当前能解决i问题的专家数量,sol[i]==0则i问题未解决,仔细想想为什么这样 
		
		int nex = x + 1;//同样,避免多次递归,提高效率 
		while(sol[nex] != 0 && nex <= m)nex++;
		dfs(nex , nowsum + 1);
		
		for(int j = 1 ; j <= qn[p[x][i]] ; j++)//回溯 
			sol[q[p[x][i]][j]]--;
	}
}
int main(){
	//input
	cin >> m >> n;
	for(int i = 1 ; i <= n ; i++){
		cin >> qn[i];
		for(int j = 1 ; j <= qn[i] ; j++){
			cin >> q[i][j];
			map[i][q[i][j]] = true;
		}
			
	}
	//判断包含关系 
	for(int i = 1 ; i <= n ; i ++)
		if(!out[i])
			for(int j = 1 ; j <= n ; j++)//判断i专家能解决的问题是否包含j专家 
				if(i != j && !out[j]){
					bool b = false;
					
					for(int k = 1 ; k <= m ; k++)
						if(!map[i][k] && map[j][k]){//如果有第i位专家可以解决但是第j位专家解决不了的题目就不能将第i位专家踢出 
							b = true;
							break;
						}
					if(!b){
						out[j] = true;
					}
				}
	//判断问题的唯一解  
	for(int i = 1 ; i <= m ; i ++){//枚举所有问题 
		if(sol[i] > 0)//如果这个问题已经有专家可以解决了就没必要继续往下判断
			continue;
		int k = -1;
		bool b = false;
		
		for(int j = 1 ; j <= n ; j ++)//枚举所有专家 
			if(map[j][i] && !out[j]){//如果这个问题已经有专家可以解决了并且当前第j位专家也可以解决(有两个及以上个专家可以解决此问题)就直接break 
				if(k != -1){
					b = true;
					break;
				}
				k = j;
			}
		if(b == false){
			out[k] = true;//把此专家踢出 
			sum ++;//把此专家可以解决的所有问题都加上 
			for(int j = 1 ; j <= m ; j++)//标记该专家能解决的问题 
				if(map[k][j])
					sol[j] += 1;
		}
			
	}
	//未解决的问题和 搜索范围内的专家建立链表(效率更高) 
	for(int i = 1 ; i <= n ; i++)
		if(!out[i])
			for(int j = 1 ; j <= m ; j ++)
				if(map[i][j]){
					pn[j]++;
					p[j][pn[j]] = i;
				}
	
	int beg = 1;//找到第一个没解决的问题,避免多次递归 
	while(sol[beg] != 0 && beg <= m)beg++;
	ans = 10000;
	dfs(beg , sum);
	cout << ans;
	return 0;
}

Although the code is a bit long, it is still easy to understand. If you don't fully understand it, you can read this blog (I just read this to learn!!!)

Address: https://blog.csdn.net/weixin_46304837/article/details/107851321

Guess you like

Origin blog.csdn.net/m0_61360607/article/details/132121874