差分约束系统之Bellman_Ford与Spfa判断负权回路

题目:http://poj.org/problem?id=1364


题意:就是简单的差分约束模型。


分析:首先我们必须知道,如果图中存在负权回路,那么差分约束没有可行解。而存在负权回路的条件是:图中某条边的松

弛操作的次数超过n次。对于Bellman_Ford,我们很容易解。如下:


#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
const int N = 105;
const int INF = 1<<29;

struct Edge
{
    int s,t;
    int w;
};

Edge edge[N*N];

int dist[N];
int cnt,n,m;

void add(int u,int v,int w)
{
    edge[cnt].s = u;
    edge[cnt].t = v;
    edge[cnt].w = w;
    cnt++;
}

void Relax(int s,int t,int w)
{
    if(dist[t] > dist[s] + w)
        dist[t] = dist[s] + w;
}

bool Bellman_Ford(int s)
{
    for(int i=0; i<n; i++)
        dist[i] = INF;
    dist[s] = 0;
    for(int i=0; i<n; i++)
        for(int j=0; j<cnt; j++)
            Relax(edge[j].s,edge[j].t,edge[j].w);
    for(int i=0; i<cnt; i++)
        if(dist[edge[i].t] > dist[edge[i].s] + edge[i].w)
            return true;
    return false;
}

int main()
{
    char str[5];
    while(cin>>n)
    {
        if(n == 0)  break;
        cin>>m;
        cnt = 0;
        while(m--)
        {
            int a,b,c;
            cin>>a>>b>>str>>c;
            if(str[0]=='l')
                add(a-1,a+b,c-1);
            else
                add(a+b,a-1,-c-1);
        }
        if(Bellman_Ford(0))  cout<<"successful conspiracy"<<endl;
        else                 cout<<"lamentable kingdom"<<endl;
    }
    return 0;
}

Bellman_Ford算法的时间复杂度为O(VE),算法简单,适用范围又广,虽然复杂度稍高,仍不失为一个很实用的算法。


对于判断负权回路问题,我们还是用Spfa算法最好,时间复杂度比较低。实际上Spfa跟Bellman_Ford算法差不多,Spfa

是Bellman_Ford算法的一种队列实现,大大减少了不必要的冗余计算。


下面是Spfa判断负权回路的方法:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <queue>

using namespace std;
const int N = 105;
const int INF = 1<<29;

struct Edge
{
    int to;
    int w;
    int next;
};

Edge edge[N*N];

bool vis[N];
int head[N],time[N],dist[N];

int n,m,cnt;
queue<int> Q;

void add(int u,int v,int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

void Init()
{
    cnt = 0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(time,0,sizeof(time));
}

bool Spfa(int s)
{
    for(int i=0; i<=n+1; i++)
        dist[i] = -INF;
    dist[s] = 0;
    time[s] = 1;
    vis[s] = 1;
    while(!Q.empty()) Q.pop();
    Q.push(s);
    while(!Q.empty())
    {
        int u  =Q.front();
        vis[u] = 0;
        Q.pop();
        for(int i=head[u]; ~i; i=edge[i].next)
        {
            int v = edge[i].to;
            int w = edge[i].w;
            if(dist[v] < dist[u] + w)
            {
                dist[v] = dist[u] + w;
                if(!vis[v])
                {
                    vis[v] = 1;
                    Q.push(v);
                    time[v]++;
                    if(time[v] > n)
                        return false;
                }
            }
        }
    }
    return true;
}

int main()
{
    char str[5];
    while(cin>>n)
    {
        Init();
        if(n == 0) break;
        cin>>m;
        for(int i=0; i<n+1; i++)
            add(0,i,0);
        while(m--)
        {
            int a,b,c;
            cin>>a>>b>>str>>c;
            if(str[0] == 'g')
                add(a + b + 1,a,c + 1);
            else
                add(a,a + b + 1,1 - c);
        }
        int s = 0;
        if(Spfa(s)) cout<<"lamentable kingdom"<<endl;
        else        cout<<"successful conspiracy"<<endl;
    }
    return 0;
}


当然,对于Spfa判负环,实际上还有优化:就是把判断单个点的入队次数大于n改为:如果总的点入队次数大于所有点两倍

时有负环,或者单个点的入队次数大于sqrt(点数)有负环。这样时间复杂度就降了很多了。



经典题目:

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3666

分析:本题要先取对数,然后转化为差分约束模型,再判断有没有解就可以了。注意这里时间限制紧,判负环要优化。


题目:http://poj.org/problem?id=3259

分析:典型的差分约束题目,直接判断负环就行,这里数据不大,可以直接就用单个点入队次数是否超过n判断就可以了。



题目:http://acm.hdu.edu.cn/showproblem.php?pid=1384

题意:在每个区间上至少选择个元素,构成一个集合S,使得集合S中的元素最少。


分析:设表示在区间上选择的元素的个数。那么有:,并且很明显还有:

,然后我们根据这两个不等式建图,然后就转化为最长路径问题。


#include <iostream>
#include <string.h>
#include <stdio.h>
#include <queue>

using namespace std;
const int N = 50005;
const int INF = 1<<30;

struct Edge
{
    int to;
    int w;
    int next;
};

Edge edge[4*N];

bool vis[N];
int head[N],dist[N];
int cnt,s,t;

queue<int> Q;

void Init()
{
    cnt = 0;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
}

void add(int u,int v,int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

void Spfa(int s)
{
    for(int i=0; i<N; i++)
        dist[i] = -INF;
    dist[s] = 0;
    while(!Q.empty()) Q.pop();
    Q.push(s);
    vis[s] = 1;
    while(!Q.empty())
    {
        int u = Q.front();
        vis[u] = 0;
        Q.pop();
        for(int i=head[u]; ~i; i=edge[i].next)
        {
            int v = edge[i].to;
            int w = edge[i].w;
            if(dist[v] < dist[u] + w)
            {
                dist[v] = dist[u] + w;
                if(!vis[v])
                {
                    vis[v] = 1;
                    Q.push(v);
                }
            }
        }
    }
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        Init();
        s = INF;
        t = -1;
        while(n--)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            if(u < s)  s = u;
            if(v + 1 > t) t = v + 1;
            add(u,v+1,w);
        }
        for(int i=s; i<=t; i++)
        {
            add(i,i-1,-1);
            add(i-1,i,0);
        }
        Spfa(s);
        printf("%d\n",dist[t]);
    }
    return 0;
}


用于打比赛的ACM算法模板 常用函数与STL 重要公式与定理 1. Fibonacci Number 2. Lucas Number 3. Catalan Number 4. Stirling Number(Second Kind) 5. Bell Number 6. Stirling's Approximation 7. Sum of Reciprocal Approximation 8. Young Tableau 9. 整数划分 10. 错排公式 11. 三角形内切圆半径公式 12. 三角形外接圆半径公式 13. 圆內接四边形面积公式 14. 基础数论公式 大数模板,字符读入 数论算法 1. Greatest Common Divisor最大公约数 2. Prime素数判断 3. Sieve Prime素数筛法 4. Module Inverse模逆元 5. Extended Euclid扩展欧几里德算法 6. Modular Linear Equation模线性方程(同余方程) 7. Chinese Remainder Theorem中国余数定理(互素于非互素) 8. Euler Function欧拉函数 9. Farey总数 9. Farey序列构造 10. Miller_Rabbin素数测试,Pollard_rho因式分解 图论算法 1. 最小生成树(Kruscal算法) 2. 最小生成树(Prim算法) 3. 单源最短路径(Bellman-ford算法) 4. 单源最短路径(Dijkstra算法) 5. 全源最短路径(Folyd算法) 6. 拓扑排序 7. 网络预流和最大流 8. 网络最小费用最大流 9. 网络最大流(高度标号预流推进) 10. 最大团 11. 二分图最大匹配(匈牙利算法) 12. 带权二分图最优匹配(KM算法) 13. 强连通分量(Kosaraju算法) 14. 强连通分量(Gabow算法) 15. 无向图割边割点和双连通分量 16. 最小树形图O(N^3) 17. 最小树形图O(VE) 几何算法 1. 几何模板 2. 球面上两点最短距离 3. 三点圆心坐标 4. 三角形几个重要的点 专题讨论 1. 树状数组 2. 字典树 3. 后缀树 4. 线段树 5. 并查集 6. 二叉堆 7. 逆序数(归并排序) 8. 树状DP 9. 欧拉路 10. 八数码 11. 高斯消元法 12. 字符串匹配(KMP算法) 13. 全排列,全组合 14. 二维线段树 15. 稳定婚姻匹配 16. 后缀数组 17. 左偏树 18. 标准RMQ-ST 19. 度限制最小生成树 20. 最优比率生成树(0/1分数规划) 21. 最小花费置换 22. 区间K大数 23. LCA - RMQ-ST 24. LCA – Tarjan 25. 指数型母函数 26. 指数型母函数(大数据) 27. 单词前缀树(字典树+KMP) 28. FFT(大数乘法) 29. 二分图网络最大流最小割 30. 混合图欧拉回路 31. 无源汇上下界网络流 32. 二分图最小点权覆盖 33. 带约束的轨道计数(Burnside引理) 34. 三分法函数波峰 35. 单词计数,矩阵乘法 36. 字符串和数值hash 37. 滚动队列,前向星表示法 38. 最小点基,最小权点基
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值