• 【洛谷P4589】[TJOI2018]智力竞赛(二分+最小链覆盖)


    洛谷

    题意:
    给出一个(DAG),现在要选出(n+1)条可相交的链来覆盖,最终使得未被覆盖的点集中,权值最小的点的权值最大。

    思路:

    • 显然最终的答案具有单调性,故直接二分答案来判断;
    • 直接将小于二分权值的点加入图中,求出最小链覆盖即可。

    这个题貌似有点卡常。。二分上界设为INF直接T飞了。。

    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/6 10:20:13
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 500 + 5;
    
    int n, m, T;
    int v[N];
    int g[N][N];
    int match[N], vis[N];
    int tmp[N], tot;
    
    int dfs(int u) {
        for(int i = 1; i <= tot; i++) {
            if(vis[i] != T && g[tmp[u]][tmp[i]]) {
                vis[i] = T;
                if(match[i] == -1 || dfs(match[i])) {
                    match[i] = u;
                    return 1;
                }
            }
        }   
        return 0;
    }
    
    bool chk(int x) {
        tot = 0;
        for(int i = 1; i <= m; i++) if(v[i] < x) tmp[++tot] = i;
        int ans = tot;
        for(int i = 1; i <= tot; i++) match[i] = -1;
        for(int i = 1; i <= tot; i++) {
            ++T; ans -= dfs(i);
        }
        return ans <= n + 1;
    }
    
    void run(){
        cin >> n >> m;
        int Max = 0;
        for(int i = 1; i <= m; i++) {
            cin >> v[i];
            Max = max(Max, v[i]);
            int k; cin >> k;
            for(int j = 1; j <= k; j++) {
                int x; cin >> x;
                g[i][x] = 1;
            }
        }
        for(int k = 1; k <= m; k++)
            for(int i = 1; i <= m; i++)
                for(int j = 1; j <= m; j++) 
                    g[i][j] |= (g[i][k] & g[k][j]);
        if(chk(INF)) {
            cout << "AK" << '
    ';
            return;
        }
        int l = 0, r = Max + 1, mid;
        while(l < r) {
            mid = (l + r) >> 1;
            if(chk(mid)) l = mid + 1;
            else r = mid;
        }
        cout << l - 1 << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
    	return 0;
    }
    
  • 相关阅读:
    SQLSERVER Truncate使用注意事项
    SQLSERVER Truncate使用注意事项
    c#事件使用示例详解
    c#事件使用示例详解
    C#反射の反射泛型
    C#反射の反射泛型
    C#反射设置属性值和获取属性值
    1336:【例31】找树根和孩子
    1336:【例31】找树根和孩子
    1037:计算2的幂
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11809873.html
Copyright © 2020-2023  润新知