码迷,mamicode.com
首页 > 其他好文 > 详细

[WC2007]剪刀石头布

时间:2019-04-28 10:03:33      阅读:131      评论:0      收藏:0      [点我收藏+]

标签:最大   getc   false   loser   info   print   需要   img   min   

技术图片


题解

给出一个竞赛图的一些边,你需要定向剩下的边,使得形成的三元环最少
\(i\)如果能赢\(j\)就连一条\(i \to j\)的边
可以发现直接统计三元环的总个数是十分困难的
我们可以考虑反面计数
\(n\)个点的竞赛图的三元环的最大个数为\(C_{n}^{3}\)
我们只需要考虑去掉每三个点不能形成三元环的条件就行了
有三条边形不成三元环的条件就是一个点的入度/出度\(>1\)
所以如果一个点的入度为\(k\)
那么ta的影响就是\(C_{k}^{3}\)
然后差分一下\(C_{k}^{3}-C_{k-1}^{3}=k-1\)
也就是说入度每增加\(1\)
那么答案就会增加当前的入度
这样我们就可以跑费用流了
从源点连向每条边,流量为1,费用为0
从每条边连向两个端点,流量为1,费用为0
从每个点连\(n-1\)条边到\(T\),流量为\(0\sim n-2\)
最小费用最大流即可

代码

// d[u] 表示入度 
// 如果i赢了j,那么++d[j] 
// 流谁谁输 
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 105 ;
const int N = 5205 ;
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 ;
}

bool exist[N] ;
int n , cnt , S , T ;
int C[M][M] , ans , num = 1 , hea[N] ;
int d[M] , sit[M][M] , id[M][M] ;
int dis[N] , pre[N] ;

struct E {
    int nxt , to , dis , cst ;
} edge[N * 50] ;
inline void Insert_edge(int from , int to , int dis , int cst) {
    edge[++num].nxt = hea[from] ; edge[num].to = to ;
    edge[num].dis = dis ; edge[num].cst = cst ; hea[from] = num ;
}
inline void add_edge(int u , int v , int w , int c) {
    Insert_edge(u , v , w , c) ;
    Insert_edge(v , u , 0 , -c) ;
}
inline bool spfa() {
    queue < int > q ; q.push(S) ; pre[T] = -1 ;
    memset(dis , 31 , sizeof(dis)) ; dis[S] = 0 ; 
    while(!q.empty()) {
        int u = q.front() ; q.pop() ; exist[u] = false ;
        for(int i = hea[u] ; i ; i = edge[i].nxt) {
            int v = edge[i].to ; 
            if(dis[v] > dis[u] + edge[i].cst && edge[i].dis > 0) {
                dis[v] = dis[u] + edge[i].cst ; pre[v] = i ;
                if(!exist[v]) { exist[v] = true ; q.push(v) ; }
            }
        }
    }
    return (pre[T] > 0) ;
}
inline void mcmf() {
    while(spfa()) {
        int diss = INF ;
        for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to) diss = min(diss , edge[pre[i]].dis) ;
        for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to) edge[pre[i]].dis -= diss , edge[pre[i] ^ 1].dis += diss ;
        ans -= dis[T] * diss ;
    }
}
int main() {
    n = read() ; S = 0 ;
    for(int i = 0 ; i <= n ; i ++) {
        C[i][0] = 1 ;
        for(int j = 1 ; j <= i ; j ++)
            C[i][j] = C[i - 1][j] + C[i - 1][j - 1] ;
    }   
    ans = C[n][3] ;
    for(int i = 1 ; i <= n ; i ++)
        for(int j = 1 ; j <= n ; j ++)
            sit[i][j] = read() ;
    cnt = n ;
    for(int i = 1 ; i <= n ; i ++)
        for(int j = i + 1 ; j <= n ; j ++) {
            if(sit[i][j] == 1) {
                ++ d[j] ;
                ans -= d[j] - 1 ;
            }
            else if(sit[i][j] == 0) {
                ++ d[i] ;
                ans -= d[i] - 1 ;
            }
            else {
                ++ cnt ;
                id[i][j] = id[j][i] = cnt ;
                add_edge(S , cnt , 1 , 0) ;
                add_edge(cnt , i , 1 , 0) ;
                add_edge(cnt , j , 1 , 0) ;
            }
        }
    T = ++ cnt ;
    for(int i = 1 ; i <= n ; i ++)
        for(int j = d[i] + 1 ; j < n ; j ++)
            add_edge(i , T , 1 , j - 1) ;       
    mcmf() ;
    printf("%d\n",ans) ;
    for(int i = 1 ; i <= n ; i ++)
        for(int j = i + 1 , _loser ; j <= n ; j ++)
            if(sit[i][j] == 2) {
                int u = id[i][j] ;
                for(int k = hea[u] ; k ; k = edge[k].nxt) {
                    int v = edge[k].to ; 
                    if(v >= 1 && v <= n && edge[k].dis == 0) {
                        _loser = v ;
                        break ;
                    } 
                }
                if(_loser == i) sit[i][j] = 0 , sit[j][i] = 1 ;
                else sit[i][j] = 1 , sit[j][i] = 0 ;
            } 
    for(int i = 1 ; i <= n ; i ++) {
        for(int j = 1 ; j <= n ; j ++)
            printf("%d ",sit[i][j]) ;
        printf("\n") ;
    }
    return 0 ;
}

[WC2007]剪刀石头布

标签:最大   getc   false   loser   info   print   需要   img   min   

原文地址:https://www.cnblogs.com/beretty/p/10781579.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!