bzoj 5335: [TJOI2018]智力竞赛 二分+最小路径覆盖

题意

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

第一行有两个整数n, m。(n ≤ 50, m ≤ 500)
接下来m行,第i+1行表示编号为i的题目的题目信息;
格式如下vi, ki, ai_1, ai_2, …, ai_ki 。
其中vi表示该题目的价值,ki 表示这个题目的后续,
ai_1, ai_2, …, ai_ki 表示这i 个题目的后续题目编号。
1 < n ≤ 50, 1 < m ≤ 500, vi ≤ 10^9, ki, ai_j ≤ m。

分析

给了这么多其实有一个条件,这是一个有向的DAG图(题目不可能循环吧。。。)
如果AK的话,就是要你求可重复点的最小路径覆盖
如果不是AK的话,二分一下,也变成求AK的情况
然后:

= N

可重复点的话跑一次floyd就行了

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 510;
const int inf = 1e9;
inline int read()
{
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}
bool f[N][N]; int match[N]; bool chw[N]; int n,m; int v[N];
bool dfs(int val,int x)
{
  for(int i=1;i<=m;i++) if(f[x][i] && !chw[i] && v[i] < val)
  {
    chw[i] = 1;
    if(match[i] == 0 || dfs(val,match[i])){match[i] = x; return 1;}
  }
  return 0;
}
bool check(int val)
{
  memset(match,0,sizeof(match)); int ans = 0; int x = 0;
  for(int i=1;i<=m;i++) if(v[i] < val)
  {
    x++; memset(chw,0,sizeof(chw));
    if(dfs(val,i)) ans++;
  }
  return x - ans <= n+1;
}
int main()
{
  n = read(); m = read();
  for(int i=1;i<=m;i++)
  {
    v[i] = read(); int k = read();
    for(int j=1;j<=k;j++){int x = read(); f[i][x] = 1;}
  }
  for(int k=1;k<=m;k++) for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) f[i][j] |= f[i][k] & f[k][j];
  int l = 1; int r = inf; int ret = inf;
  check(5);
  while(l<=r)
  {
    int mid = (l+r)>>1;
    if(check(mid)) l=mid+1,ret=mid;
    else r=mid-1;
  }
  if(ret==inf) puts("AK");
  else printf("%d\n",ret);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39708759/article/details/80467894
今日推荐