• 笔记


    此笔记开始记录时间为2019.7.20
    主要供自己深化知识点

    一、Floyd算法:

    流程(重点理解):

    通常我们写的Floyd算法框架是这样的

    for(int k=1;k<=n;++k)
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
    

    Floyd算法能如此优美是由于其为dp算法
    式子如此简单以至于我们能轻而易举将其背下
    大部分人对其了解只限于——三重循环 k 放最外层 i j 放内层
    但是这个算法并不是这么的显然
    首先
    说这是个dp算法
    因为存在转移方程——d[i][j]=min(d[i][j],d[i][k]+d[k][j])
    可以感性理解一下:
    对不起我太菜了

    稍稍证明一下:
    设d[i][j][k]为i->j路径只经过1-k节点的最短路
    且d[i][j][0]为i->j的初始路径
    则若经过k节点
    d[i][j][k]=d[i][k][k-1]+d[k][j][k-1]
    否则
    d[i][j][k]=d[i][j][k-1]
    最后滚动数组就可以得出最终式了

    严格证明 这篇讲的真的很好
    dp最初形式

    用法总结:

    1. 最短路:最基本用法,套框架即可

    2. 传递闭包:
      通过Floyd确定各两者的二元关系
      经典例题:

    POJ3660 Cow Contest
    题目大意:有N头牛,评以N个等级,各不相同,先给出部分牛的等级的高低关系,问最多能确定多少头牛的等级
    解题思路:一头牛的等级,当且仅当它与其它N-1头牛的关系确定时确定,于是我们可以将牛的等级关系看做一张有向图,然后跑Floyd,得到任意两点的关系,再对每一头牛进行检查即可

    1. 无向图最小环:
      运用Floyd求无向图中的最小环
      例题:

    POJ1734 Sightseeing trip
    题目大意:给一张图,求图中长度最小的环,输出环中节点
    解题思路:最小环长度为min{d[i][j]+e[i][k]+e[k][j]}(执行此步时,要在更新d[i][j]前)
    p.s. 求最小环还有一个更优秀的算法:
    先求最小生成树 枚举边 用lca处理 算出最小环

    1. 求无向图删除一些边,使最短路不变,最多可删多少边
      这个好理解
      若i-j有一条边
      且i-j存在一条路径长度小于该边
      则该边可删除
      题目可以自己找找

    附上代码:

    POJ3660 Cow Contest

    //poj 3660 cow contest
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    #define inf 100000000
    #define min(x,y) ((x)<(y)?(x):(y))
    #define max(x,y) ((x)>(y)?(x):(y))
    #define rep(i,a,b) for(int i=(a);i<=(b);++i)
    #define dwn(i,a,b) for(int i=(a);i>=(b);--i)
    using namespace std;
    typedef long long ll;
    int n,m,ans=0;
    bool d[110][110];
    void floyd(int n)
    {
        rep(k,1,n)
            rep(i,1,n)
                rep(j,1,n)
                    d[i][j]|=d[i][k]&d[k][j];
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        rep(i,1,m)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            d[u][v]=1;
        }
        floyd(n);
        rep(i,1,n){
            rep(j,1,n)
            {
                if(i==j) continue;
                if((!(d[i][j]||d[j][i]))||(d[i][j]&&d[j][i])){ans++;break;}
            }
        }
        printf("%d
    ",n-ans);
        return 0;
    }
    

    POJ1734 Sightseeing Trip

    //poj 1734 sightseeing trip
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    #define inf 0x1f1f1f1f
    #define rep(i,a,b) for(int i=(a);i<=(b);++i)
    #define dwn(i,a,b) for(int i=(a);i>=(b);--i)
    using namespace std;
    typedef long long ll;
    int n,m,ans,cnt,way[110],d[110][110],e[110][110],path[110][110];
    void floyd()
    {
        rep(k,1,n)
        {
            rep(i,1,k-1)
            {
                rep(j,i+1,k-1)
                {
                    if(ans>d[i][j]+e[i][k]+e[k][j])
                    {
                        ans=d[i][j]+e[i][k]+e[k][j];
                        cnt=0;
                        int t=j;
                        while(t!=i)
                        {
                            way[++cnt]=t;
                            t=path[i][t];
                        }
                        way[++cnt]=i;
                        way[++cnt]=k;//此时就要把路径记录下来 因为path会在循环中变化
                    }
                }
            }
            rep(i,1,n)
            {
                rep(j,1,n)
                {
                    if(d[i][j]>d[i][k]+d[k][j])
                    {
                        d[i][j]=d[i][k]+d[k][j];
                        path[i][j]=path[k][j];//path i j 表示此时从i到j的路j的前一节点
                    }
                }
            }
        }
    }
    int main()
    {
        while(scanf("%d%d",&n,&m)!=EOF){
            ans=inf;
            memset(d,0x1f,sizeof(d));
            memset(e,0x1f,sizeof(e));
            rep(i,1,n)
            {
                e[i][i]=d[i][i]=0;
                rep(j,1,n)
                {
                    path[i][j]=i;
                }
            }
            rep(i,1,m)
            {
                int u,v,dis;
                scanf("%d%d%d",&u,&v,&dis);
                d[v][u]=d[u][v]=e[v][u]=e[u][v]=min(e[u][v],dis);
            }
            floyd();
            if(ans==inf)
                printf("No solution.
    ");
            else
            {
                rep(i,1,cnt)
                {
                    printf("%d ",way[i]);
                }
                printf("
    ");
            }
        }
        return 0;
    }
    

    二、差分约束系统

    概念:

    对于一组不等式组:

    [egin{cases} x1-x2le -1\ x2-x3le 3\ x1-x4le 4\ x4-x3le 1\ x5-x4le -4\ x1-x5le 2\ x3-x5le 4\ x2-x5le -3\ end{cases} ]

    这样的不等式组称为差分约束系统
    要么有无数解 要么无解
    因为若存在一组解{x1,x2,x3,...}
    则必定存在解{x1+k,x2+k,x3+k,...}

    解法:

    首先引入三角形不等式
    对于有向边<u,v>
    若dis[x]为源点到x点最短路
    则有(dis[v]le dis[u]+edge(u,v))

    我们发现这个式子可转化为
    (dis[v]-dis[u]le edge(u,v))
    与不等式组中式子模式很相似
    于是我们可以利用这个性质
    构建图形解决这种问题

    构图:

    即对于(xv-xule k)构建一条<u,v>有向边 权值为k

    求一遍最短路即可得到一种解

    当且仅当图中存在负环时 无解

    判负环:当一个点入队次数>所有点个数 存在负环

    选取源点:

    选取源点太麻烦了
    于是我们新增一个源点x0
    在不等式组中加上一堆条件

    [egin{cases} x1-x0le 0\ x2-x0le 0\ ......\ xn-x0le 0 end{cases} ]

    若设x0初始值为k
    则答案均小于等于k
    且所有点的dis值都能求出

    解决:

    最后用spfa求最短路即为一组解
    注意判负环

    对于另一类问题:

    [egin{cases} x1-x2ge a1\ x2-x3ge a2\ ... end{cases} ]

    可以两边乘-1
    就变成第一种问题

    但是比较麻烦
    发现可以用最长路的三角形不等式构图
    即对于有向边<u,v>,dis[x]表示源点到x点的最长路
    则dis[v]>=dis[u]+edge(u,v)
    同理用spfa求最长路 判正环

    值得一提的是
    最短路求出的是不超过x0的最大值
    最长路求出的是不低于x0的最小值
    在下面一道题中读者能有更深刻的体会

    例题:

    [SCOI2011]糖果

    思路:
    由于每个人都要有糖所以源点值设为1,保证每个人糖数至少1
    再跑一遍最长路累加答案就行了
    求出来肯定是最小值是因为这是满足条件的dis的最小值
    p.s. 这道题其实正解不是spfa 而是 tarjan缩点+拓扑排序
    spfa法要加特判,特判自环有长度大于0的自环直接输出-1

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    #include<map>
    #define inf 2000000000
    #define rep(i,a,b) for(int i=(a);i<=(b);++i)
    #define dwn(i,a,b) for(int i=(a);i>=(b);--i)
    using namespace std;
    typedef long long ll;
    int n,m,dis[100010],cnt[100010];
    bool v[100010];
    int tot=1,head[100010];
    struct EDGE
    {
        int to,nxt,d;
    }edge[200010];
    ll ans=0;
    queue<int> que;
    void add(int u,int v,int d)
    {
        edge[++tot].to=v;
        edge[tot].nxt=head[u];
        edge[tot].d=d;
        head[u]=tot;
    }
    bool spfa()
    {
        rep(i,1,n)
            dis[i]=1,v[i]=1,que.push(i);
        while(!que.empty())
        {
            int x=que.front();v[x]=0;que.pop();
            for(int i=head[x];i;i=edge[i].nxt)
            {
                int y=edge[i].to;
                if(dis[y]<dis[x]+edge[i].d)
                {
                    dis[y]=dis[x]+edge[i].d;
                    if(!v[y])
                    {
                        v[y]=1;
                        que.push(y);
                        if(++cnt[y]>n) return 0;
                    }
                }
            }
        }
        return 1;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        rep(i,1,m)
        {
            int x,a,b;
            scanf("%d%d%d",&x,&a,&b);
            if(x==1) add(a,b,0),add(b,a,0);
            if(x==2) add(a,b,1);
            if(x==3) add(b,a,0);
            if(x==4) add(b,a,1);
            if(x==5) add(a,b,0);
            if(x%2==0&&a==b){
                printf("-1
    ");
                return 0;
            }
        }
        if(spfa())
        {
            rep(i,1,n)
                ans+=dis[i];
            printf("%lld
    ",ans);
        }
        else
        {
            printf("-1
    ");
        }
        return 0;
    }
    

    留个大坑 有时间更

  • 相关阅读:
    构建之法阅读笔记
    10个操作数的随机四则运算测试
    poj 1742 Coins (动态规划,背包问题)
    第二周进度报告
    蓝桥杯 2015年省赛最后一题 生命之树(树形dp)
    蓝桥杯 ALGO-108 最大体积 (动态规划)
    蓝桥杯 algo——6 安慰奶牛 (最小生成树)
    蓝桥杯 algo_5 最短路 (bellman,SPFA)
    蓝桥杯 ALGO-4 结点选择 (树形动态规划)
    软件工程——评价搜狗拼音输入法
  • 原文地址:https://www.cnblogs.com/MYsBlogs/p/11219805.html
Copyright © 2020-2023  润新知