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

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


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


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

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


#include 
#include 
#include 

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 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"<

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


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

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


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

#include 
#include 
#include 
#include 

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 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>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"<


当然,对于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 
#include 
#include 
#include 

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 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 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;
}


你可能感兴趣的:(图论)