BZOJ 2597 剪刀石头布(最小费用最大流)(WC2007)

Description

在一些一对一游戏的比赛(如下棋、乒乓球和羽毛球的单打)中,我们经常会遇到 A 胜过 B B 胜过 C C 又胜过 A 的有趣情况,不妨形象的称之为剪刀石头布情况。有的时候,无聊的人们会津津乐道于统计有多少这样的剪刀石头布情况发生,即有多少对无序三元组 (A, B, C) ,满足其中的一个人在比赛中赢了另一个人,另一个人赢了第三个人而第三个人又胜过了第一个人。注意这里无序的意思是说三元组中元素的顺序并不重要,将 (A, B, C) (A, C, B) (B, A, C) (B, C, A) (C, A, B) (C, B, A) 视为相同的情况。
N 个人参加一场这样的游戏的比赛,赛程规定任意两个人之间都要进行一场比赛:这样总共有 场比赛。比赛已经进行了一部分,我们想知道在极端情况下,比赛结束后最多会发生多少剪刀石头布情况。即给出已经发生的比赛结果,而你可以任意安排剩下的比赛的结果,以得到尽量多的剪刀石头布情况。

Input

输入文件的第 1 行是一个整数 N ,表示参加比赛的人数。
之后是一个 N N 列的数字矩阵:一共 N 行,每行 N 列,数字间用空格隔开。
在第 (i+1) 行的第 j 列的数字如果是 1 ,则表示 i 在已经发生的比赛中赢了 j ;该数字若是 0 ,则表示在已经发生的比赛中 i 败于 j ;该数字是 2 ,表示 i j 之间的比赛尚未发生。数字矩阵对角线上的数字,即第 (i+1) 行第 i 列的数字都是 0 ,它们仅仅是占位符号,没有任何意义。
输入文件保证合法,不会发生矛盾,当 i j 时,第 (i+1) 行第 j 列和第 (j+1) 行第 i 列的两个数字要么都是 2 ,要么一个是 0 一个是 1

Output

输出文件的第 1 行是一个整数,表示在你安排的比赛结果中,出现了多少剪刀石头布情况。
输出文件的第 2 行开始有一个和输入文件中格式相同的 N N 列的数字矩阵。第 (i+1) 行第 j 个数字描述了 i j 之间的比赛结果, 1 表示 i 赢了 j 0 表示 i 负于 j ,与输入矩阵不同的是,在这个矩阵中没有表示比赛尚未进行的数字 2 ;对角线上的数字都是 0 。输出矩阵要保证合法,不能发生矛盾。
 
PS:这题太牛叉了值得一做……
 
代码(896MS):
  1 #include <cstdio>

  2 #include <cstring>

  3 #include <iostream>

  4 #include <algorithm>

  5 #include <queue>

  6 using namespace std;

  7 

  8 const int MAXN = 110;

  9 const int MAXV = MAXN * MAXN;

 10 const int MAXE = MAXN * MAXV;

 11 const int INF = 0x7f7f7f7f;

 12 

 13 struct ZWK_FLOW {

 14     int head[MAXV], dis[MAXV];

 15     int to[MAXE], next[MAXE], flow[MAXE], cost[MAXE];

 16     int n, ecnt, st, ed;

 17 

 18     void init() {

 19         memset(head, 0, sizeof(head));

 20         ecnt = 2;

 21     }

 22 

 23     void add_edge(int u, int v, int c, int w) {

 24         to[ecnt] = v; flow[ecnt] = c; cost[ecnt] = w; next[ecnt] = head[u]; head[u] = ecnt++;

 25         to[ecnt] = u; flow[ecnt] = 0; cost[ecnt] = -w; next[ecnt] = head[v]; head[v] = ecnt++;

 26         //printf("%d %d %d %d\n", u, v, c, w);

 27     }

 28 

 29     void spfa() {

 30         for(int i = 1; i <= n; ++i) dis[i] = INF;

 31         priority_queue<pair<int, int> > que;

 32         dis[st] = 0; que.push(make_pair(0, st));

 33         while(!que.empty()) {

 34             int u = que.top().second, d = -que.top().first; que.pop();

 35             if(d != dis[u]) continue;

 36             for(int p = head[u]; p; p = next[p]) {

 37                 int &v = to[p];

 38                 if(flow[p] && dis[v] > d + cost[p]) {

 39                     dis[v] = d + cost[p];

 40                     que.push(make_pair(-dis[v], v));

 41                 }

 42             }

 43         }

 44         int t = dis[ed];

 45         for(int i = 1; i <= n; ++i) dis[i] = t - dis[i];

 46     }

 47 

 48     int minCost, maxFlow;

 49     bool vis[MAXV];

 50 

 51     int add_flow(int u, int aug) {

 52         if(u == ed) {

 53             maxFlow += aug;

 54             minCost += dis[st] * aug;

 55             return aug;

 56         }

 57         vis[u] = true;

 58         int now = aug;

 59         for(int p = head[u]; p; p = next[p]) {

 60             int &v = to[p];

 61             if(flow[p] && !vis[v] && dis[u] == dis[v] + cost[p]) {

 62                 int t = add_flow(v, min(now, flow[p]));

 63                 flow[p] -= t;

 64                 flow[p ^ 1] += t;

 65                 now -= t;

 66                 if(!now) break;

 67             }

 68         }

 69         return aug - now;

 70     }

 71 

 72     bool modify_label() {

 73         int d = INF;

 74         for(int u = 1; u <= n; ++u) if(vis[u]) {

 75             for(int p = head[u]; p; p = next[p]) {

 76                 int &v = to[p];

 77                 if(flow[p] && !vis[v]) d = min(d, dis[v] + cost[p] - dis[u]);

 78             }

 79         }

 80         if(d == INF) return false;

 81         for(int i = 1; i <= n; ++i) if(vis[i]) dis[i] += d;

 82         return true;

 83     }

 84 

 85     int min_cost_flow(int ss, int tt, int nn) {

 86         st = ss, ed = tt, n = nn;

 87         minCost = maxFlow = 0;

 88         spfa();

 89         while(true) {

 90             while(true) {

 91                 for(int i = 1; i <= n; ++i) vis[i] = false;

 92                 if(!add_flow(st, INF)) break;

 93             }

 94             if(!modify_label()) break;

 95         }

 96         return minCost;

 97     }

 98 } G;

 99 

100 int n, m;

101 int mat[MAXN][MAXN], ans[MAXN][MAXN];

102 

103 inline int encode(int i, int j) {

104     if(i > j) swap(i, j);

105     return i * n + j;

106 }

107 

108 int main() {

109     scanf("%d", &n);

110     for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) scanf("%d", &mat[i][j]);

111     m = n * n;

112     int ss = n + m + 1, tt = ss + 1;

113     G.init();

114     int sum = n * (n - 1) * (n - 2) / 6;

115     for(int i = 1; i <= n; ++i) {

116         for(int j = 1, tmp = 1; j < n; ++j, tmp += 2) G.add_edge(ss, i, 1, tmp);

117         for(int j = 1; j <= n; ++j) if(mat[i][j] != 0)

118             ans[i][j] = G.ecnt, G.add_edge(i, encode(i, j), 1, 0);

119     }

120     for(int i = 1; i <= m; ++i) G.add_edge(i + n, tt, 1, 0);

121     int x = G.min_cost_flow(ss, tt, tt);

122     printf("%d\n", sum - (x - n * (n - 1) / 2) / 2);

123     for(int i = 1; i <= n; ++i) {

124         for(int j = 1; j <= n; ++j) {

125             if(j != 1) printf(" ");

126             if(mat[i][j] != 2) printf("%d", mat[i][j]);

127             else {

128                 if(G.flow[ans[i][j]] == 0) printf("1");

129                 else printf("0");

130             }

131         }

132         puts("");

133     }

134 }
View Code

 

 

 

你可能感兴趣的:(2007)