洛谷传送门
BZOJ传送门
题目描述
在一些一对一游戏的比赛(如下棋、乒乓球和羽毛球的单打)中,我们经常会遇到 A A 胜过 , B B 胜过 而 C C 又胜过 的有趣情况,不妨形象的称之为剪刀石头布情况。有的时候,无聊的人们会津津乐道于统计有多少这样的剪刀石头布情况发生,即有多少对无序三元组 (A,B,C) ( A , B , C ) ,满足其中的一个人在比赛中赢了另一个人,另一个人赢了第三个人而第三个人又胜过了第一个人。注意这里无序的意思是说三元组中元素的顺序并不重要,将 (A,B,C) ( A , B , C ) 、 (A,C,B) ( A , C , B ) 、 (B,A,C) ( B , A , C ) 、 (B,C,A) ( B , C , A ) 、 (C,A,B) ( C , A , B ) 和 (C,B,A) ( C , B , A ) 视为相同的情况。
有 N N 个人参加一场这样的游戏的比赛,赛程规定任意两个人之间都要进行一场比赛:这样总共有 场比赛。比赛已经进行了一部分,我们想知道在极端情况下,比赛结束后最多会发生多少剪刀石头布情况。即给出已经发生的比赛结果,而你可以任意安排剩下的比赛的结果,以得到尽量多的剪刀石头布情况。
输入输出格式
输入格式:
输入文件的第 1 1 行是一个整数 ,表示参加比赛的人数。
之后是一个 N N 行 列的数字矩阵:一共 N N 行,每行 列,数字间用空格隔开。
在第 (i+1) ( i + 1 ) 行的第 j j 列的数字如果是 ,则表示 i i 在已经发生的比赛中赢了 ;该数字若是 0 0 ,则表示在已经发生的比赛中 败于 j j ;该数字是 ,表示 i i 和 之间的比赛尚未发生。数字矩阵对角线上的数字,即第 (i+1) ( i + 1 ) 行第 i i 列的数字都是 ,它们仅仅是占位符号,没有任何意义。
输入文件保证合法,不会发生矛盾,当 i≠j i ≠ j 时,第 (i+1) ( i + 1 ) 行第 j j 列和第 行第 i i 列的两个数字要么都是 ,要么一个是 0 0 一个是 。
输出格式:
输出文件的第 1 1 行是一个整数,表示在你安排的比赛结果中,出现了多少剪刀石头布情况。
输出文件的第 行开始有一个和输入文件中格式相同的 N N 行 列的数字矩阵。第 (i+1) ( i + 1 ) 行第 j j 个数字描述了 和 j j 之间的比赛结果, 表示 i i 赢了 , 0 0 表示 负于 j j ,与输入矩阵不同的是,在这个矩阵中没有表示比赛尚未进行的数字 ;对角线上的数字都是 0 0 。输出矩阵要保证合法,不能发生矛盾。
输入输出样例
输入样例#1:
3
0 1 2
0 0 2
2 2 0
输出样例#1:
1
0 1 0
0 0 1
1 0 0
说明
【评分标准】
对于每个测试点,仅当你的程序的输出第一行的数字和标准答案一致,且给出了一个与之一致的合法方案,你才能得到该测试点的满分,否则该测试点得 分。
【数据范围】
30 %的数据中, N≤6 N ≤ 6 ;
100 %的数据中, N≤100 N ≤ 100 。
解题分析
首先, 我们可以知道总的选择方案数为 (N3)=N×(N−1)×(N−2)6 ( N 3 ) = N × ( N − 1 ) × ( N − 2 ) 6 。
不妨将 A A 赢过看做一条由 A A 连向的有向边。设 degA d e g A 为点 A A 的入度, 那么减少的方案数就为(因为随便两个赢过 A A 的人和都不可能再出现贡献, 而且由于我们这样的统计方式, 并不会重复计算)。这样我们想要尽量减少减少的方案数, 与最小费用最大流的思想契合。
这样就好办了。 我们要做的是其实就是给无向边定向, 给无向边的其中一个点加上 1 1 的入度。 进一步发现, 于是我们从每个点向汇点连出费用为 1,2,3,⋯ 1 , 2 , 3 , ⋯ ,流量为 1 1 的边, 表示入度为的情况下的减少的方案数;将每条边视为一个点, 从源点向其连费用为0,流量为1的边,向边两端连费用为0,流量为1的边, 表示入度只能加在其中一个点上。注意原来本来定了向的边不用处理。
最后输出方案的时候看哪条边流量为0即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <queue>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 20050
#define base 105
#define S 0
#define T 20000
#define INF 100000000
template <class TT>
IN void in(TT &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
struct Edge{int to, fl, cost, nex;} edge[MX << 4];
int head[MX], layer[MX], dis[MX], del[MX], pre[MX],
mp[105][105], rec[105][105], deg[MX];
bool inq[MX];
int dot, cnt = -1, ans;
IN void add(R int from, R int to, R int fl, R int cost)
{
edge[++cnt] = {to, fl, cost, head[from]}, head[from] = cnt;
edge[++cnt] = {from, 0, -cost, head[to]}, head[to] = cnt;
}
IN int getid(R int x, R int y) {return (x - 1) * dot + y + base;}
namespace MCMF
{
std::queue <int> q;
IN bool SPFA()
{
std::memset(dis, 63, sizeof(dis));
dis[S] = 0; del[S] = INF; q.push(S); R int now;
W (!q.empty())
{
now = q.front(); q.pop();
for (R int i = head[now]; ~i; i = edge[i].nex)
{
if(edge[i].fl > 0 && dis[edge[i].to] > dis[now] + edge[i].cost)
{
dis[edge[i].to] = dis[now] + edge[i].cost;
del[edge[i].to] = std::min(edge[i].fl, del[now]);
pre[edge[i].to] = i;
if(!inq[edge[i].to]) q.push(edge[i].to), inq[edge[i].to] = true;
}
}
inq[now] = false;
}
return dis[T] < INF;
}
IN void updata()
{
ans -= del[T] * dis[T];
R int now = T, pr;
W (now != S)
{
pr = pre[now];
edge[pr].fl -= del[T];
edge[pr ^ 1].fl += del[T];
now = edge[pr ^ 1].to;
}
}
IN void init() {W (SPFA()) updata();}
}
int main(void)
{
std::memset(head, -1, sizeof(head));
in(dot); int buf; ans = (dot - 2) * (dot - 1) * dot / 6;
for (R int i = 1; i <= dot; ++i)
for (R int j = 1; j <= dot; ++j)
{in(mp[i][j]); if(mp[i][j] == 1) ++deg[j];}
for (R int i = 1; i < dot; ++i)
for (R int j = i + 1; j <= dot; ++j)
{
if(mp[i][j] == 2)
{
buf = getid(i, j);
add(S, buf, 1, 0);
add(buf, i, 1, 0);
rec[j][i] = cnt - 1;
add(buf, j, 1, 0);
rec[i][j] = cnt - 1;//预存边的编号
}
}
for (R int i = 1; i <= dot; ++i)
{
ans -= deg[i] * (deg[i] - 1) / 2;
for (R int j = deg[i] + 1; j < dot; ++j) add(i, T, 1, j - 1);
}
MCMF::init();
printf("%d\n", ans);
for (R int i = 1; i <= dot; ++i)
{
for (R int j = 1; j <= dot; ++j)
{
if(mp[i][j] < 2) printf("%d", mp[i][j]);
else printf("%d", edge[rec[j][i]].fl);
if(j < dot) putchar(32);
}
puts("");
}
}