zoukankan      html  css  js  c++  java
  • 「TJOI2018」智力竞赛(二分+DAG最小可相交路径覆盖)

    https://loj.ac/problem/2574

    这个题目描述扎心了。

    简要题意:
    用n+1条可以相交的路径去覆盖DAG,使得没被覆盖的点的权值的最小值最大。

    首先二分答案,问题转换为有一些点一定要被覆盖,问n+1条路径内有没有解。

    这个可以暴力费用流,每个点拆成两个点,(i->i',r=1),如果这个点必选,则费用为inf,否则为0。
    由于每个点还可以被额外经过无限次,所以再连(i->i',r=inf,w=0)
    若有边((i,j)),则(i'->j)连边,每个点都可以作为终点或者起点,所以(S->i,i'->T)连边。
    求总流量不超过n+1时的最大费用,看能不能把inf都选上。

    事实上这是个经典问题:DAG路径覆盖问题
    https://www.cnblogs.com/justPassBy/p/5369930.html

    先Floyd传递闭包,只保留要被覆盖的点,跑dinic即可。

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int inf = 1e9;
    
    const int N = 505;
    
    int n, m;
    int v[N];
    
    int f[N][N];
    
    const int M = N * N * 3;
    
    int fi[M], nt[M], to[M], r[M], tot = 1;
    int S, T;
    
    void link(int x, int y, int z) {
    //	pp("%d %d %d
    ", x, y, z);
    	nt[++ tot] = fi[x], to[tot] = y, r[tot] = z, fi[x] = tot;
    	nt[++ tot] = fi[y], to[tot] = x, r[tot] = 0, fi[y] = tot;
    }
    
    void cl() {
    	fo(i, 1, T) fi[i] = 0;
    	tot = 1;
    }
    
    int p[N], p0;
    
    int d[M], d0, dis[M], cur[M];
    
    int bfs() {
    	fo(i, 1, T) dis[i] = inf;
    	d[d0 = 1] = S; dis[S] = 0;
    	for(int i = 1; i <= d0; i ++) {
    		int x = d[i];
    		cur[x] = fi[x];
    		for(int j = fi[x]; j; j = nt[j]) if(r[j] && dis[to[j]] == inf) {
    			dis[to[j]] = dis[x] + 1;
    			d[++ d0] = to[j];
    		}
    	}
    	return dis[T] < inf;
    }
    
    int dfs(int x, int flow) {
    	if(x == T) return flow;
    	int use = 0;
    	for(int i = cur[x]; i; cur[x] = i = nt[i]) if(r[i] && dis[x] + 1 == dis[to[i]]) {
    		int t = dfs(to[i], min(flow - use, r[i]));
    		use += t, r[i] -= t, r[i ^ 1] += t;
    		if(flow == use) return use;
    	}
    	return use;
    }
    
    int pd(int mi) {
    	S = 2 * m + 1, T = S + 1;
    	cl();
    	p0 = 0;
    	fo(i, 1, m) if(v[i] < mi) {
    		p[++ p0] = i;
    		link(S, i, 1);
    		link(i + m, T, 1);
    	}
    	fo(i, 1, p0) fo(j, 1, p0) if(i != j && f[p[i]][p[j]] < inf) {
    		link(p[i], p[j] + m, 1);
    	}
    	int sum = 0;
    	while(bfs()) sum += dfs(S, 1 << 30);
    	sum = p0 - sum;
    	return sum <= n;
    }
    
    int main() {
    	scanf("%d %d", &n, &m);
    	n ++;
    	fo(i, 1, m) fo(j, 1, m) if(i != j) f[i][j] = 1e9;
    	fo(i, 1, m) {
    		int k, j;
    		scanf("%d %d", &v[i], &k);
    		fo(u, 1, k) {
    			scanf("%d", &j);
    			f[i][j] = 1;
    		}
    	}
    	fo(k, 1, m) fo(i, 1, m) fo(j, 1, m)
    		f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
    	int as = 0;
    	for(int l = 0, r = 1e9 + 1; l <= r; ) {
    		int m = l + r >> 1;
    		if(pd(m)) as = m, l = m + 1; else r = m - 1;
    	}
    	if(as > 1e9) pp("AK
    "); else {
    		pp("%d
    ", as);
    	}
    }
    
  • 相关阅读:
    解决:std::ostream operator<< should have been declared inside 'xxx'
    c++ friend 遇到 namespace 无法访问 private 成员的问题
    Compiler Error C2872: ambiguous symbol
    【持续更新】总结:C++开发时积累的一些零碎的东西
    陷阱:C++模块之间的”直接依赖“和”间接依赖“与Makefile的撰写
    ZThread::ThreadLocal:ERROR C4716 must return a value的解决
    java值传递
    iframe与父页面传值
    iframe父子兄弟之间调用传值(contentWindow && parent)
    MySQL返回影响行数的测试示例
  • 原文地址:https://www.cnblogs.com/coldchair/p/12675852.html
Copyright © 2011-2022 走看看