[BZOJ1143][CTSC2008]祭祀river 做题笔记

18 篇文章 0 订阅
3 篇文章 0 订阅

题目来源:http://www.lydsy.com/JudgeOnline/problem.php?id=1143
其实说是原创,也就是代码而已。。。
一种理解,二分图的最大独立集(引用自http://www.cnblogs.com/BLADEVIL/p/3713043.html):

  我们可以将一个点拆成两个点x,y,那么如果存在一条i->j的路径,我们就连接xi,yj,那么答案就是n-最大匹配数。

  因为i->j所以对于i与j只能选一个,那么我们只需要求出来二分图的最大独立集就可以了,二分图的最大独立集=点数-最大匹配。

另一种思路是最长反链(引用自http://www.bubuko.com/infodetail-664202.html,不知道原作者是谁真是太抱歉了)。

在有向无环图中,有如下的一些定义和性质:

链:一条链是一些点的集合,链上任意两个点x, y,满足要么 x 能到达 y ,要么 y 能到达 x 。

反链:一条反链是一些点的集合,链上任意两个点x, y,满足 x 不能到达 y,且 y 也不能到达 x。

那么很显然这道题就是求最长反链长度了。

一个定理:最长反链长度 = 最小链覆盖(用最少的链覆盖所有顶点)

对偶定理:最长链长度 = 最小反链覆盖

那么我们要求出的就是这个有向无环图的最小链覆盖了。最小链覆盖也就是路径可以相交的最小路径覆盖。

我们先来看路径不能相交的最小路径覆盖怎么来做:

建立一个二分图,两边都是n个点,原图的每个点 i 对应两个,在左边的叫做 i1, 在右边的叫做 i2 。

然后原图中如果存在一条边 (x, y),那么就在二分图中建立 (x1, y2) 的边。

这样建立二分图之后,原图的点数 n - 二分图最大匹配 = 原图的最小路径覆盖(路径不能相交)。

这样为什么是对的呢?我们可以认为,开始时原图的每个点都是独立的一条路径,然后我们每次在二分图中选出一条边,就是将两条路径连接成一条路径,答案数就减少1。

而路径是不能相交的,所以我们在二分图中选出的边也是不能相交的,所以就是二分图的最大匹配。

了解了路径不能相交的最小路径覆盖之后,怎么解路径可以相交的最小路径覆盖(也就是最小链覆盖)呢?

我们将原图做一次Floyd传递闭包,之后就可以知道任意两点 x, y,x 是否能到达 y。

如果两个点 x, y,满足 x 可以到达 y ,那么就在二分图中建立边 (x1, y2) 。

这样其实就是相当于将原图改造了一下,只要 x 能到达 y ,就直接连一条边 (x,
y),这样就可以“绕过”原图的一些被其他路径占用的点,直接构造新路径了。

这样就将可以相交的最小路径覆盖转化为了路径不能相交的最小路径覆盖了。


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int inf=0x3fffffff;
int map[205][205];
int maxflow=0,s,t,n,m,tot=1;
int d[400];
int head[400],e[160000],nxt[160000],ver[160000];
void add (int u,int v,int w) {
    ver[++tot]=v;e[tot]=w;nxt[tot]=head[u];head[u]=tot;
    ver[++tot]=u;e[tot]=0;nxt[tot]=head[v];head[v]=tot;
}
void floyd () {
    int i,j,k;
    for (k=1;k<=n;k++)
        for (i=1;i<=n;i++) 
            for (j=1;j<=n;j++) 
                map[i][j]|=(map[i][k]&map[k][j]);
}
bool bfs () {
    queue <int> q;
    memset(d,0,sizeof(d));
    q.push(s); d[s]=1;
    while (!q.empty()) {
        int x=q.front(); q.pop();
        for (int i=head[x];i;i=nxt[i])
            if (e[i]&&!d[ver[i]]) {
                q.push(ver[i]);
                d[ver[i]]=d[x]+1;
                if (ver[i]==t) return 1;
            }
    }
    return 0;
}
int dinic (int x,int f) {
    int rest=f;
    if (x==t) return f;
    for (int i=head[x];i&&rest;i=nxt[i]) 
        if (e[i]&&d[ver[i]]==d[x]+1) {
            int now=dinic(ver[i],min(e[i],rest));
            if (!now) d[ver[i]]=0;
            e[i]-=now;
            e[i^1]+=now;
            rest-=now;
        }
    return f-rest;
}
void build () {
    for (int i=1;i<=n;i++) 
        add(s,i,1),add(i+n,t,1);
    for (int i=1;i<=n;i++) 
        for (int j=1;j<=n;j++) 
            if (map[i][j]) add(i,j+n,inf);
}
int main () {
    int tmp,u,v;
    scanf("%d%d",&n,&m);
    s=0,t=2*n+1;
    for (int i=1;i<=m;i++) {
        scanf("%d%d",&u,&v);
        map[u][v]=1;
    }
    floyd();
    build();
    while (bfs()) 
        while (tmp=dinic(s,inf)) maxflow+=tmp;
    printf("%d\n",n-maxflow);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交流和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值