[TJOI2018]智力竞赛 (匈牙利)

description

题目描述
小豆报名参加智力竞赛,他带上了 n个好朋友作为亲友团一块来参加比赛。
比赛规则如下:一共有 m道题目,每个人都有 1 次答题机会,每次答题为选择一道题目回答,在回答正确后,可以从这个题目的后续题目,直到题目答错题目或者没有后续题目。
每个问题都会代表一个价值,比赛最后的参赛选手获得奖励价值等价于该选手和他的亲友团没有回答的问题中的最低价值。
我们现在知道小豆和他的亲友团实力非常强,能够做出这次竞赛中的所有题目。
小豆想知道在知道题目和后续题目的条件下,他最大能获得价值是多少?

输入格式
第一行有两个整数 n, m
接下来 m行,第 i+1 行表示编号为 i的题目的题目信息;
格式如下 vi,ki,ai,1,ai,2,…,ai,ki
其中 vi表示该题目的价值, ki 表示这个题目的后续, ai,1,ai,2,…,ai,ki 表示这 i个题目的后续题目编号。

输出格式
如果全部题目都能答对,则输出 AK ,否则输出小豆可以获得的最高奖励价值。

样例
样例输入 1

1 3
1 0
2 1 3
3 0

AK

样例输入 2

1 6
1 2 2 3
2 1 4
3 1 4
4 1 6
5 0
6 0

5

数据范围与提示
对于 10%的数据, 1≤n,m≤10 。
对于 20% 的数据, 1≤n,m≤100 。
对于 100% 的数据, 1≤n≤50,1≤m≤500,vi≤109,ki,ai,j≤m

solution

说实话没读懂题面
在这里插入图片描述
一句话题意:给出一个带权有向图,选出 n + 1 n+1 n+1条链
能否全部点覆盖,如果不能,不能覆盖的点权最小值最大是多少

最小值最大,套路又整二分上
考虑二分不能覆盖的最小值
就转化为了 D A G DAG DAG的最小不相交路径覆盖条数
也就可以转化为二分图最大匹配,最大匹配数就是可合并的路径条数
点数➖最大匹配数=最小不相交路径覆盖数

如果二分结果大于最大值,就说明每道题都能被覆盖到,就 A K AK AK

code

#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 505
vector < int > G[maxn];
int n, m;
int val[maxn], match[maxn];
int g[maxn][maxn], f[maxn][maxn];
bool vis[maxn];

bool find( int u ) {
    
    
	for( int i = 0;i < G[u].size();i ++ ) {
    
    
		int v = G[u][i];
		if( vis[v] ) continue;
		vis[v] = 1;
		if( ! match[v] || find( match[v] ) ) {
    
    
			match[v] = u;
			return 1;
		}
	}
	return 0;
}

bool check( int x ) {
    
    
	memset( match, 0, sizeof( match ) );
	for( int i = 1;i <= m;i ++ ) G[i].clear();
	memset( f, 0, sizeof( f ) );
	for( int i = 1;i <= m;i ++ )
		for( int j = 1;j <= m;j ++ )
			if( val[i] <= x && val[j] <= x )
				f[i][j] = g[i][j];
	for( int k = 1;k <= m;k ++ )
		for( int i = 1;i <= m;i ++ )
			if( f[i][k] )
				for( int j = 1;j <= m;j ++ )
					f[i][j] |= f[i][k] & f[k][j];
	int tot = 0;
	for( int i = 1;i <= m;i ++ ) {
    
    
		if( val[i] > x ) continue;
		tot ++;
		for( int j = 1;j <= m;j ++ )
			if( f[i][j] ) G[i].push_back( j );
	}
	for( int i = 1;i <= m;i ++ ) {
    
    
		if( val[i] > x ) continue;
		memset( vis, 0, sizeof( vis ) );
		if( find( i ) ) tot --;
	}
	return tot <= n;
}

int main() {
    
    
	scanf( "%d %d", &n, &m );n ++;
	int l = 1e9, r = 0;
	for( int i = 1, c, x;i <= m;i ++ ) {
    
    
		scanf( "%d %d", &val[i], &c );
		l = min( l, val[i] ), r = max( r, val[i] );
		while( c -- ) scanf( "%d", &x ), g[i][x] = 1;
	}
	for( int k = 1;k <= m;k ++ )
		for( int i = 1;i <= m;i ++ )
			if( g[i][k] ) //不加此优化 时间复杂度就会跑满m^3log 
				for( int j = 1;j <= m;j ++ )
					g[i][j] |= g[i][k] & g[k][j];
	int maxx = r;
	while( l <= r ) {
    
    
		int mid = ( l + r ) >> 1;
		if( check( mid ) ) l = mid + 1;
		else r = mid - 1;
	}
	if( l > maxx ) printf( "AK\n" );
	else printf( "%d\n", l );
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Emm_Titan/article/details/113730671
今日推荐