• [八省联考2018]劈配


    题目背景

    一年一度的综艺节目《中国新代码》又开始了。Zayid 从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了。

    题目描述

    轻车熟路的Zayid 顺利地通过了海选,接下来的环节是导师盲选,这一阶段的规则是这样的:

    总共n 名参赛选手(编号从1 至n)每人写出一份代码并介绍自己的梦想。接着 由所有导师对这些选手进行排名。为了避免后续的麻烦,规定不存在排名并列的情况。

    同时,每名选手都将独立地填写一份志愿表,来对总共 m 位导师(编号从 1 至 m)作出评价。志愿表上包含了共m 档志愿。对于每一档志愿,选手被允许填写最多C 位导师,每位导师最多被每位选手填写一次(放弃某些导师也是被允许的)。

    在双方的工作都完成后,进行录取工作。每位导师都有自己战队的人数上限,这意味着可能有部分选手的较高志愿、甚至是全部志愿无法得到满足。节目组对”前i 名的录取结果最优“ 作出如下定义:

    前1 名的录取结果最优,当且仅当第1 名被其最高非空志愿录取(特别地,如 果第1 名没有填写志愿表,那么该选手出局)。

    前i 名的录取结果最优,当且仅当在前i - 1 名的录取结果最优的情况下:第i 名 被其理论可能的最高志愿录取(特别地,如果第i 名没有填写志愿表、或其所有 志愿中的导师战队均已满员,那么该选手出局)。

    如果一种方案满足‘‘前n 名的录取结果最优’’,那么我们可以简称这种方案是最 优的。

    每个人都有一个自己的理想值si,表示第i 位同学希望自己被第si 或更高的志愿录取,如果没有,那么他就会非常沮丧。

    现在,所有选手的志愿表和排名都已公示。巧合的是,每位选手的排名都恰好与它们的编号相同。

    对于每一位选手,Zayid 都想知道下面两个问题的答案:

    在最优的录取方案中,他会被第几志愿录取。

    在其他选手相对排名不变的情况下,至少上升多少名才能使得他不沮丧。

    作为《中国新代码》的实力派代码手,Zayid 当然轻松地解决了这个问题。不过他还是想请你再算一遍,来检验自己计算的正确性。

    输入输出格式

    输入格式:

    从文件mentor.in 中读入数据。

    每个测试点包含多组测试数据,第一行 2 个用空格隔开的非负整数 T;C,分别表示数据组数、每档志愿最多允许填写的导师数目。

    接下来依次描述每组数据,对于每组数据:

    第1 行两个用空格隔开的正整数n , m。

    n,m 分别表示选手的数量、导师的数量。

    第2 行m 个用空格隔开的正整数:其中第i 个整数为 (b[i]) , 表示编号为i 的导师战队人数的上限。

    第3 行至第n + 2 行,每行m 个用空格隔开的非负整数:其中第i + 2 行左起第 j 个数为 (a[i][j]).

    表示编号为i 的选手将编号为j 的导师编排在了第(a[i][j]) 志愿。

    特别地,如果 a_{i,j}​ = 0,则表示该选手没有将该导师填入志愿表。

    第n + 3 行n 个用空格隔开的正整数,其中第i 个整数为 s[i]

    表示编号为i 的选手的理想值。

    在这一部分,保证 (s[i]<=m)

    输出格式:

    输出到文件mentor.out 中。

    按顺序输出每组数据的答案。对于每组数据,输出2 行:

    第1 行输出n 个用空格隔开的正整数,其中第i 个整数的意义为:

    在最优的录取方案中,编号为i 的选手会被该档志愿录取。

    特别地,如果该选手出局,则这个数为m + 1。

    第 2 行输出 n 个用空格隔开的非负整数,其中第 i 个整数的意义为:

    使编号为i 的选手不沮丧,最少需要让他上升的排名数。

    特别地,如果该选手一定会沮丧,则这个数为i。

    输入输出样例

    输入样例#1:

    3 5
    2 2
    1 1
    2 2
    1 2
    1 1
    2 2
    1 1
    1 2
    1 2
    2 1
    2 2
    1 1
    0 1
    0 1
    2 2

    输出样例#1:

    2 1
    1 0
    1 2
    0 1
    1 3
    0 1

    输入样例#2:

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

    输出样例#2:

    1 1 3 2
    0 0 0 0


    网络流

    但是感觉挺麻烦的

    我调了快一天了==

    话说我以前好像没大写过动态加边

    但是这道题不动态加边是错的

    因为在退流时可以从与这个同学无关的边流过来

    话说这题建图倒是没啥难度

    就是从源点向每个人连条流量为1的边

    导师向汇点连一条流量为导师战队人数上限的边

    如果学生在第i志愿选到了导师,其他的志愿就没有任何连边的意义了

    所以我们存下在第i个同学选完后的图

    然后我们就对于每个学生按照志愿从小到大连边

    每次加入该同学的所有第t志愿选择的导师

    一旦有增广路就退出输出答案

    并且删掉除了第t志愿的边并记录图

    这样就解决了第一问

    考虑用二分处理第二问

    只需要二分出ta在第k名时可以选到ta所希望选到的志愿

    然后直接用我们上面所记录的有k - 1个同学选完的图

    然后连上第i同学志愿<=ta所希望的边check

    这样第二问就完成辣

    话说这题好麻烦啊

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    const int M = 210 ;
    const int N = 10005 ;
    const int INF = 1e9 ;
    using namespace std ;
    inline int read() {
        char c = getchar() ; int x = 0 , w = 1 ;
        while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
        while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
        return x*w ;
    }
    int n , m , S , T ;
    int b[M] , a[M][M] , Num[M][M] , vol[M][M][M] , wish[M] ;
    queue < int > q ;
    struct Graph {
        int d[M<<1] , hea[M<<1] , num ;
        struct E {
            int Nxt , to , Dis ;
        }edge[N] ;
        inline void _Link(int from , int to , int dis) {
            edge[++num].Nxt = hea[from] ; edge[num].to = to ;
            edge[num].Dis = dis ; hea[from] = num ;
        }
        inline void add_edge(int from , int to , int dis) { 
            _Link(from , to , dis) ; _Link(to , from , 0) ;
        }
        inline bool Bfs() {
        	while(!q.empty()) q.pop() ;
            memset(d , 0 , sizeof(d)) ;
            d[S] = 1 ; q.push(S) ;
            while(!q.empty()) {
                int u = q.front() ; q.pop() ;
                for(int i = hea[u] ; i ; i = edge[i].Nxt) {
                    int v = edge[i].to ;
                    if(!d[v] && edge[i].Dis > 0) {
                        d[v] = d[u] + 1 ; q.push(v) ;
                        if(v == T) return true ;
                    }
                }
            }
            return d[T] ;
        }
        int Dfs(int u , int Flow) {
            if(u == T || !Flow) return Flow ;
            int Sum = 0 ;
            for(int i = hea[u] ; i ; i = edge[i].Nxt) {
                int v = edge[i].to ;
                if(d[v] == d[u] + 1 && edge[i].Dis > 0) {
                    int diss = Dfs(v , min(Flow , edge[i].Dis)) ;
                    if(diss > 0) {
                        edge[i].Dis -= diss ; edge[i^1].Dis += diss ;
                        Sum += diss ; Flow -= diss ;
                        if(!Flow) break ;
                    }
                }
            }
            if(!Sum) d[u] = -1 ;
            return Sum ;
        }
        inline int Dinic() {
            bool Ans = false ;
            if(Bfs()) Dfs(S , INF) , Ans = true ;
            return Ans ; 
        }
    }G[M];
    inline void Clear() {
        memset(G , 0 , sizeof(G)) ;
        memset(Num , 0 , sizeof(Num)) ;
        memset(vol , 0 , sizeof(vol)) ;
    }
    inline bool Check(int x , int i) {
        G[n + 1] = G[x - 1] ;
        G[n + 1].add_edge(S , i , 1) ;
        for(int j = 1 ; j <= wish[i] ; j ++)
            for(int k = 1 ; k <= Num[i][j] ; k ++)
                G[n + 1].add_edge(i , vol[i][j][k] , 1) ;
        return G[n + 1].Dinic() ;
    }
    int main() {
        int Q = read() ; read() ;
        while(Q--) {
            Clear() ;
            n = read() ; m = read() ;
            S = 0 , T = n + m + 1 ; G[0].num = 1 ;
            for(int i = 1 ; i <= m ; i ++) {
                b[i] = read() ;
                G[0].add_edge(i + n , T , b[i]) ;
            }
            for(int i = 1 ; i <= n ; i ++)
                for(int j = 1 ; j <= m ; j ++) {
                	a[i][j] = read() ;
                	++Num[i][a[i][j]] ;
                	vol[i][a[i][j]][Num[i][a[i][j]]] = j + n ;
                }
    //      Num[i][j]表示第i个人的第j志愿报了几个导师
    //      vol[i][j][k]表示第i个人的第j志愿的第k个是几号导师			
            for(int i = 1 ; i <= n ; i ++) wish[i] = read() ;
            for(int i = 1 ; i <= n ; i ++) // 第几个人 
                for(int j = 1 ; j <= m ; j ++) { // 第几志愿 
                	G[i] = G[i - 1] ; G[i].num = G[i - 1].num ;
                	G[i].add_edge(S , i , 1) ;
                	for(int k = 1 ; k <= Num[i][j] ; k ++) // 哪位导师 
                		G[i].add_edge(i , vol[i][j][k] , 1) ;
                    if(G[i].Dinic()) {
                    	printf("%d ",j) ;
                    	break ;
                    }
                    if(j == m) printf("%d ",m + 1) ;
                }
            printf("
    ") ;
            for(int i = 1 ; i <= n ; i ++) {
                int l = 0 , r = i ;
                while(l < r) {
                    int mid = (l + r)>>1 ;
                    if(Check(i - mid , i)) r = mid ;
                    else l = mid + 1 ;
                }
                printf("%d ", l) ;
            }
            printf("
    ") ;
        }
        return 0 ;
    }
    
  • 相关阅读:
    Android获取两条线之间的夹角度数
    Android字体度量(FontMetrics)
    Android下如何计算要显示的字符串所占的宽度和高度
    Android 颜色渲染PorterDuff及Xfermode详解
    从输入URL到页面加载的全过程
    滚动优化
    常用的前端相关chrome插件
    DNS预解析prefetch
    资源预加载preload和资源预读取prefetch简明学习
    使用chrome开发者工具中的performance面板解决性能瓶颈
  • 原文地址:https://www.cnblogs.com/beretty/p/9507307.html
Copyright © 2020-2023  润新知