题目链接

题意

对于一个竞赛图(有向完全图),其顶点是选手,边是比赛,边\(e=(u,v)\)代表该场比赛中\(u\)战胜\(v\)。

现定义选手的分数为其战胜的人的个数(即竞赛图中点的出度)。并且定义\(strong\ king\)为这样的选手,他战胜了所有比他分数高的人。

给定各选手的分数序列,问这个分数序列对应的所有可能的比赛局面中,最多有多少个\(strong\ king\)?

思路

从定义着手——建图

\(strong\ king\)为这样的选手,他战胜了所有比他分数高的人

这意味着什么?

这意味着对于所有发生在\(u,v\)之间的比赛,如果\(u\)是\(strong\ king\)并且\(score(u)<score(v)\),那么在原竞赛图中有边\((u,v)\)(即\(u\)战胜\(v\)),对\(u\)的分数的贡献为\(1\).

那么可不可以拆点建图呢?拆成胜者和败者?

不可以,因为无法保证边\((u,v)\)和边\((v,u)\)不共存

此时注意到,选手与比赛之间的关系是可以通过限制保证其唯一性的,于是如下建图:

  1. 在 源点 到 选手(\(n\)个) 之间连边,边权为选手的分数

  2. 在 比赛(\(\frac{n*(n-1)}{2}场\)) 到 汇点 之间连边,边权为\(1\)

  3. 在 选手\(i,j\) 到 发生在\(i,j\)间的比赛 之间连边:

    如果确定某个人赢了这场比赛,那么就连确定的一条边,否则两条都连。

然而,我怎么知道谁赢了这场比赛呢?

即,哪些比赛的结果是确定的呢?

也就是,哪些人是\(strong\ king\)呢?

提供建图的条件——枚举

注意到,\(n\leq 10\),于是可以考虑从大到小枚举\(strong\ king\)的个数\(k\)。

对于每一个\(k\),再去枚举其每一个子集,在此基础上建图进行\(check\).

如果有一个子集可以满流,就意味着这个\(k\)是可行的。

一个巧妙的结论——优化

事实上,对于每一个\(k\),可以不用枚举其每一个子集,而只要看分数最高的\(k\)个人,令他们皆为\(strong\ king\)然后建图。

结论

如果该分数序列可以对应到一个有\(k\)名\(strong\ king\)的比赛局面,那么必然存在这样一个局面,其中

\(k\)名\(strong\ king\)为分数最高的\(k\)人。

证明

采用逐步调整法。

假设存在一个有\(k\)名\(strong\ king\)的局面,但他们并不是连续的分数最高的k人,则存在\(i,j, score(i) < score(j)\), \(i\)是\(strong\ king\)而\(j\)不是。

这意味着在分数高于\(i\)的人中存在\(t\)个人,\(x_1,x_2,...,x_t\),\(i\)未战胜他们,而\(j\)战胜了他们。

现进行调整,使得\(i\)战胜了\(x_1,x_2,...,x_t\),\(j\)未战胜\(x_t\),则

  1. \(i\)的分数增加了\(t\)
  2. \(j\)的分数减少了\(1\)
  3. \(x_t\)的分数守恒
  4. \(x_1,x_2,...x_{t-1}\)的分数均减少了\(1\)

于是在分数不大于\(i\)的人的范围内,再找\(t\)个人,满足\(i\)战胜了他们,而\(j,x_1,x_2,...,x_{t-1}\)未战胜他们。反转这些局面,即可使得所有分数守恒。

// 至于是否能够找到这\(t\)个人,我暂时并不会证...Orz

Code

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#define maxn 1010
#define inf 0x3f3f3f3f
using namespace std;
typedef long long LL;
struct Edge { int to, ne, c; }edge[maxn];
int dep[maxn], ne[maxn], n,tot, s,t, a[maxn];
void add(int u, int v, int c) {
edge[tot] = {v, ne[u], c};
ne[u] = tot++;
edge[tot] = {u, ne[v], 0};
ne[v] = tot++;
}
int bfs(int src) {
memset(dep, 0, sizeof dep);
dep[src] = 1;
queue<int> q;
while (!q.empty()) q.pop();
q.push(src);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = ne[u]; ~i; i = edge[i].ne) {
int v = edge[i].to;
if (edge[i].c > 0 && !dep[v]) dep[v] = dep[u] + 1, q.push(v);
}
}
return dep[t];
}
int dfs(int u, int flow) {
if (u == t) return flow;
int ret = 0;
for (int i = ne[u]; ~i; i = edge[i].ne) {
int v = edge[i].to;
if (edge[i].c > 0 && dep[v] == dep[u] + 1) {
int c = dfs(v, min(flow-ret, edge[i].c));
edge[i].c -= c;
edge[i^1].c += c;
ret += c;
if (ret == flow) break;
}
}
if (!flow) dep[u] = 0;
return ret;
}
bool getInt(int& x) {
char ch = getchar();
while (!isdigit(ch)) {
if (ch=='\n') return false;
ch = getchar();
}
x = ch-'0';
return true;
}
bool tryy(int x) {
tot=0; memset(ne,-1,sizeof(ne));
s=0, t=n+n*(n-1)/2+1;
for (int i = 1; i <= n; ++i) add(s, i, a[i]);
for (int i = 1; i <= n*(n-1)/2; ++i) add(n+i, t, 1);
int u=1, v=2;
for (int i = 1; i <= n*(n-1)/2; ++i) {
if (u >= x && a[v] > a[u]) add(u, n+i, 1);
else add(u, n+i, 1), add(v, n+i, 1);
++v; if (v==n+1) v = ++u+1;
}
int ans=0, ret=0;
while (bfs(s)) {
while (ret = dfs(s, inf)) ans += ret;
}
return ans == n*(n-1)/2;
}
void work() {
n = 0; while (getInt(a[++n])); --n;
for (int i = n; i >= 1; --i) if (tryy(n-i+1)) {
printf("%d\n", i); return;
}
}
int main() {
int T;
scanf("%d\n", &T);
while (T--) work();
return 0;
}

后话

其实我有些迷茫,不是很清楚博客写得这么详尽有多大的意义...

更何况,这个方法还并不是自己想出来的而是看了别人的博客得到的...

并且,有写这些的时间其实可以用来再写一道题了...

但是怎么说呢...

在写博客的时候,在理思路与组织文字的时候,事实上是有反思这个思考的过程的。

一步一步写下来,就觉得是个很顺畅的思路了,是应当且能够想出来的。

毕竟,做题不仅是积累经验,更重要的是训练思维啊。

但愿这是有用的吧。

还请大家多多批评指正。

一起变得更加强吧。

poj 2699 The Maximum Number of Strong Kings 枚举 最大流的更多相关文章

  1. POJ 2699 The Maximum Number of Strong Kings (最大流+枚举)

    http://poj.org/problem?id=2699 题意: 一场联赛可以表示成一个完全图,点表示参赛选手,任意两点u, v之间有且仅有一条有向边(u, v)或( v, u),表示u打败v或v ...

  2. poj 2699 The Maximum Number of Strong Kings【最大流+枚举】

    因为n很小所以从大到小枚举答案.(从小到大先排个序,因为显然胜利场次越多越容易成为strong king.然后对于每个枚举出来的ans建图.点分别表示人和比赛.s向所有人连接流量为胜利场次的边,所有比 ...

  3. POJ 2699 The Maximum Number of Strong Kings Description

    The Maximum Number of Strong Kings   Description A tournament can be represented by a complete graph ...

  4. POJ - 2699 The Maximum Number of Strong Kings (最大流+枚举)

    题意:有n(n<=10)个选手,两两之间打比赛,共有n*(n-1)/2场比赛,赢一场得1分.给出每个人最后的得分.求有多少个定义如下的strong king:赢了所有得分比自己高的人或本身就是分 ...

  5. POJ 2699 The Maximum Number of Strong Kings ——网络流

    一定存在一种最优方案,使得分数前几个人是SK 所以我们可以二分答案或者枚举,然后就是经典的网络流建模. 另:输入很Excited #include <cstdio> #include &l ...

  6. POJ2699:The Maximum Number of Strong Kings(枚举+贪心+最大流)

    The Maximum Number of Strong Kings Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 2488 ...

  7. POJ2699 The Maximum Number of Strong Kings(最大流)

    枚举所有Strong King的状态(最多1024种左右),然后判断是否合法. 判定合法用网络流,源点-比赛-人-汇点,这样连边. 源点向每场比赛连容量为1的边: 如果一场比赛,A和B,A是Stron ...

  8. POJ2699 The Maximum Number of Strong Kings

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 2102   Accepted: 975 Description A tour ...

  9. 【POJ2699】The Maximum Number of Strong Kings(网络流)

    Description A tournament can be represented by a complete graph in which each vertex denotes a playe ...

随机推荐

  1. 【Python学习之九】asyncio—异步IO

    asyncio 这是python3.4引入的标准库,直接内置对异步IO的支持.asyncio的编程模型就是一个消息循环.从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程 ...

  2. 基于js原生封装的点击显示完整文字

    基于js原生封装的点击显示完整文字 (function(window) { var inner = ''; var showCont_s = function(ele) { this.init.app ...

  3. python PEP8代码规范及问题

    最近刚刚接触Python,为了养成好习惯,尽量保证自己写的代码符合PEP8代码规范,下面是过程中报出的警告及解决方法,英文有些翻译不太准确见谅,会不断更新: PEP 8: module level i ...

  4. A1058 A+B in Hogwarts (20)(20 分)

    A1058 A+B in Hogwarts (20)(20 分) If you are a fan of Harry Potter, you would know the world of magic ...

  5. C#+VisionPro连接相机获取图像的两种方式

    两种比较常用的方式. C#直接连接相机获取图像(GIGE) 在获取图像前,需要先创建一个相机对象,再使用这个相机对象的Acquire方法拍摄照片. ICogAcqFifo macqfifo;//定义相 ...

  6. laravel5.2总结--邮件

    laravel自带SwiftMailer库,集成了多种邮件API,支持多种邮件驱动方式,包括smtp.Mailgun.Maildrill.Amazon SES.mail和sendmail,Mailgu ...

  7. Asp.net页面生命周期详解任我行(3)-服务器处理请求详细过程

    前言 百度了一下才知道,传智的邹老师桃李满天下呀,我也是邹老师的粉丝,最开始学习页面生命周期的时候也是看了邹老师的视频. 本人是参考了以下前辈的作品,本文中也参合了本人心得,绝非有意盗版,旨在传播,最 ...

  8. Asp.net页面生命周期详解任我行(1)-小试牛刀,编写页面代码

    前言 很久很久以前,还是我在学校的时候,我就看了传智里面视频,学习了一下Asp.net页面生命周期,当时看的时候,因为内功不够深厚,看起来很吃力,现在回头温习了一下,还是有点收获的,于是想用博客记录一 ...

  9. virsh命令管理虚拟机

    virsh命令管理虚拟机 libvirt有两种控制方式,命令行和图形界面. 1.图形界面:通过执行名virt-manager,启动libvirt的图形界面,在图形界面下可以一步一步的创建虚拟机,管理虚 ...

  10. 【Set Matrix Zeros】cpp

    题目: Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. cl ...