zoukankan      html  css  js  c++  java
  • [bzoj2597][Wc2007]剪刀石头布_费用流

    [Wc2007]剪刀石头布

    题目大意https://www.lydsy.com/JudgeOnline/problem.php?id=2597


    题解

    发现直接求三元环不好求,我们考虑任选三个点不是三元环的个数。

    这样的话,必定是有一个点被其余两个点指,我们就根据这个来求。

    又发现,最后的答案之和所有点的度数有关。

    就是,$sum C_{d_i}^{2}$。

    紧接着,因为度数和是一定的。而且已经有了一些边。

    现在就是有固定的度数可以分配,每个点有一个分配上限,怎么分配最少?

    发现一个事,就是$C_{d_i + 1}^{2} - C_{d_i} ^ {2} < C_{d_i + 2}^{2} - C_{d_i + 1}^{2}$。

    根据这个性质,我们就可以暴力连边费用流了。

    我们就把权值的差当做费用,它肯定会从低往高走因为是最小费用,满足题意。

    代码

    #include <bits/stdc++.h>
    
    #define N 11010 
    
    #define M 500010 
    
    using namespace std;
    
    int head[N], to[M], pre[N], nxt[M], val[M], cst[M], tot = 1;
    
    int S = N - 1, T = N - 2, dis[N];
    
    bool vis[N];
    
    queue <int> q;
    
    char *p1, *p2, buf[100000];
    
    #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
    
    int rd() {
    	int x = 0;
    	char c = nc();
    	while (c < 48) {
    		c = nc();
    	}
    	while (c > 47) {
    		x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
    	}
    	return x;
    }
    
    inline void add2(int x, int y, int z, int w) {
    	to[ ++ tot] = y;
    	nxt[tot] = head[x];
    	val[tot] = z;
    	cst[tot] = w;
    	head[x] = tot;
    }
    
    inline void add(int x, int y, int z, int w) {
    	add2(x, y, z, w);
    	add2(y, x, 0, -w);
    }
    
    bool spfa() {
    	while (!q.empty()) {
    		q.pop();
    	}
    	memset(pre, 0, sizeof pre);
    	memset(dis, 0x3f, sizeof dis);
    	dis[S] = 0;
    	q.push(S);
    	while (!q.empty()) {
    		int x = q.front();
    		q.pop();
    		vis[x] = false;
    		for (int i = head[x]; i; i = nxt[i]) {
    			if (dis[to[i]] > dis[x] + cst[i] && val[i]) {
    				dis[to[i]] = dis[x] + cst[i];
    				pre[to[i]] = i ^ 1;
    				if (!vis[to[i]]) {
    					vis[to[i]] = true;
    					q.push(to[i]);
    				}
    			}
    		}
    	}
    	return pre[T];
    }
    
    int mincost() {
    	int re = 0;
    	while (spfa()) {
    		int mdl = 0x3f3f3f3f;
    		for (int i = T; i != S; i = to[pre[i]]) {
    			mdl = min(mdl, val[pre[i] ^ 1]);
    		}
    		for (int i = T; i != S; i = to[pre[i]]) {
    			val[pre[i] ^ 1] -= mdl;
    			val[pre[i]] += mdl;
    			re += mdl * cst[pre[i] ^ 1];
    		}
    	}
    	return re;
    }
    
    int A[110][110], id[110][110];
    
    int main() {
    	int n = rd();
    	for (int i = 1; i <= n; i ++ ) {
    		for (int j = 1; j <= n; j ++ ) {
    			A[i][j] = rd();
    		}
    	}
    	int cnt = n;
    	for (int i = 1; i <= n; i ++ ) {
    		for (int j = i + 1; j <= n; j ++ ) {
    			if (A[i][j] == 2) {
    				cnt ++ ;
    				add(cnt, i, 1, 0);
    				id[i][j] = tot - 1;
    				add(cnt, j, 1, 0);
    				id[j][i] = tot - 1;
    				add(S, cnt, 1, 0);
    			}
    		}
    	}
    	// cout << tot << ' ' << cnt << endl ;
    	for (int i = 1; i <= n; i ++ ) {
    		int c = 0;
    		for (int j = 1; j <= n; j ++ ) {
    			if (A[i][j] == 1) {
    				c ++ ;
    			}
    		}
    		if (c) {
    			add(S, i, c, 0);
    		}
    	}
    	for (int i = 1; i <= n; i ++ ) {
    		for (int j = 1; j <= n; j ++ ) {
    			add(i, T, 1, j * (j - 1) / 2 - (j - 1) * (j - 2) / 2);
    		}
    	}
    	int ans = mincost();
    	ans = n * (n - 1) * (n -  2) / 6 - ans;
    	cout << ans << endl ;
    	for (int i = 1; i <= n; i ++ ) {
    		for (int j = 1; j <= n; j ++ ) {
    			if (A[i][j] != 2) {
    				printf("%d ", A[i][j]);
    			}
    			else {
    				if (i == j) {
    					printf("0 ");
    				}
    				else {
    					if (!val[id[i][j]]) {
    						printf("1 ");
    					}
    					else {
    						printf("0 ");
    					}
    				}
    			}
    		}
    		puts("");
    	}
    	return 0;
    }
    /*
    6
    0 2 1 0 2 1
    2 0 0 2 2 2
    0 1 0 2 1 1
    1 2 2 0 1 2
    2 2 0 0 0 2
    0 2 0 2 2 0
    
    */
    

    小结:这个费用流很巧妙。就是有先后的选取过程,但是保证后面的代价比前面的代价大,我们可以暴力建边。 

  • 相关阅读:
    多播委托和匿名方法再加上Lambda表达式
    委托
    从警察抓小偷看委托
    StringBuilder
    C#修饰符详解
    数据结构与算法之队列
    数据结构与算法之栈
    win10重复安装
    网络编程基础
    PrintPreviewControl
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11420778.html
Copyright © 2011-2022 走看看