poj 1364 King (差分约束系统)

    在找图的相关题目时找到这道题,看了半天没有发现和图有哪门子关系,看discuss里面才了解到这个叫做“差分约束系统”的东东。赶紧百度一下,学习学习。纠结了我几天,受益匪浅。花了我30多个 submit来测试。才搞懂了。。。

 

   题目大意:已知一个序列 s[1...n],给出它的 m个子序列以及对该子序列的约束条件,例如 s[a],s[a+1],s[a+2],...,s[a+b];且s[a]+s[a+1]+s[a+2]+...+s[a+b] < or > k。问题关键在于如何转化约束条件,用前n项和来转化成两两之间的关系。如:s[a]+s[a+1]+...+s[a+b] < k 可以转化成前n项和 sum[a+b]-sum[a-1] < k,为了能用Bellman_Ford,即将'<'转化成'<=',可以写成sum[a+b]-sum[a-1] <= k-1.若是 s[a]+s[a+1]+...+s[a+b] > k 可转化成 sum[a+b]-sum[a-1] >= k+1, 即 sum[a-1]-sum[a+b] <= -k-1;

Bellman-Ford算法实现:

#include <stdio.h>  
#define INF 100000000  
struct  
{  
    int s, e, v;  
} edge[105];  
int n, nedge;  
int dis[105];  
void add(int s, int e, int v)  
{  
    edge[nedge].s = s;  
    edge[nedge].e = e;  
    edge[nedge].v = v;  
    nedge++;  
}  
int relax(int s, int e, int v)  
{  
    if (dis[s]+v < dis[e])  
    {  
        dis[e] = dis[s]+v;  
        return 1;  
    }  
    return 0;  
}  
int BellmanFord(int s0)  
{  
    int i, j;  
    //Initialize  
    for (i=0; i<=n; i++)  
        dis[i] = INF;  
    dis[s0] = 0;  
    //Relax  
    for (i=1; i<=n; i++) //引入一个源点0之后就有n+1个顶点了  
        for (j=0; j<nedge; j++)  
            relax(edge[j].s, edge[j].e, edge[j].v);  
    //Check Negative power circuit  
    for (i=0; i<nedge; i++)  
        if (relax(edge[i].s, edge[i].e, edge[i].v))  
            return 1;  
    return 0;  
}  
int main()  
{  
    int  m, a, b, k;  
    char c[4];  
    while (scanf("%d", &n) && n)  
    {  
        scanf("%d", &m);  
        nedge = 0;  
        while (m--)  
        {  
            scanf("%d %d %s %d", &a, &b, c, &k);  
            if (c[0] == 'l')  
                add(a-1, a+b, k-1);  
            else  
                add(a+b, a-1, -k-1);  
        }  
        if (BellmanFord(0))  
            printf("successful conspiracy/n");  
        else  
            printf("lamentable kingdom/n");  
    }  
    return 0;  
}


SPFA算法实现:

#include <stdio.h>  
#include <string.h>  
#include <stdlib.h>  
#define INF 100000000  
struct EDGE  
{  
    int nd;  //终点  
    int eg;  //边权  
    struct EDGE *nxt;  
};  
struct  
{  
    struct EDGE *head, *last;  
} node[120];  
/*  邻接表存储 */  
int n;  
int dis[120];  
int Q[20000], vst[120], cnt[120];  
void add(int s, int e, int v)  
{  
    struct EDGE *p;  
    p = (struct EDGE*)malloc(sizeof(struct EDGE));  
    p->nd = e;  
    p->eg = v;  
    p->nxt = NULL;  
    if (node[s].head == NULL)  
    {  
        node[s].head = p;  
        node[s].last = p;  
    }  
    else  
    {  
        node[s].last->nxt = p;  
        node[s].last = p;  
    }  
}  
int relax(int s, int e, int v)  
{  
    if (dis[s]+v < dis[e])  
    {  
        dis[e] = dis[s]+v;  
        return 1;  
    }  
    return 0;  
}  
/*  队列实现SPFA  */  
int SPFA(int s0)  
{  
    int i, p, q;  
    struct EDGE *pp;  
    memset(vst, 0, sizeof(vst));  
    memset(cnt, 0, sizeof(cnt));  
    for (i=0; i<=n+1; i++)  
        dis[i] = INF;  
    dis[s0] = 0;  
    Q[0] = s0; p = 0; q = 1;  
    vst[s0] = 1;  
    cnt[s0]++;  
    while (p < q)  
    {  
        pp = node[Q[p]].head;  
        while (pp)  
        {  
            if (relax(Q[p], pp->nd, pp->eg) && !vst[pp->nd])  
            {  
                Q[q++] = pp->nd;  
                vst[pp->nd] = 1;  
                cnt[pp->nd]++;  
                if (cnt[pp->nd] > n+1)  
                    return 1;  
            }  
            pp = pp->nxt;  
        }  
        vst[Q[p]] = 0;  
        p++;  
    }  
    return 0;  
}  
int main()  
{  
    int  m, a, b, k;  
    char c[4];  
    while (scanf("%d", &n) && n)  
    {  
        memset(node, 0, sizeof(node));  
        scanf("%d", &m);  
        while (m--)  
        {  
            scanf("%d %d %s %d", &a, &b, c, &k);  
            if (c[0] == 'l')  
                add(a-1, a+b, k-1);  
            else  
                add(a+b, a-1, -k-1);  
        }  
        //附加n+1点当做源点  
        for (k=0; k<=n; k++)  
            add(n+1, k, 0);  
        if (SPFA(n+1))  
            printf("successful conspiracy/n");  
        else  
            printf("lamentable kingdom/n");  
    }  
    return 0;  
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值