hdu1531 King(经典差分约束)


http://acm.hdu.edu.cn/showproblem.php?pid=1531

题意:n个点m个关系,n代表一个序列:a[1]+a[2]+a[3]+...+a[n],m代表有多少个子序列(约束条件),每个约束条件的格式如下,s(对应序列中起点的序号) len(对应序列的长度) gt(大于约束值)/lt(小于约束值) w(约束值大小),前两个参数确定了子序列,子序列的和与约束值构成约束条件。求问在满足这些约束值的前提下,是否能够找出可以存在的原序列。


ps:要不是别人的题解,我连题都看不懂。


思路:老实说刚开始想不到差分约束。这题并没有对每一个元素给出确定值,只是问是否可以满足所有的约束条件。关键是问题的转化,原本格式是a[s1]+a[s2]+...a[s+len]和某值比较,这怎么搞?但是注意这些元素是连续的,所以可以变成S[s+len]-S[s-1],我们以前学过的等差数列技巧。这样加上比如小于的判断值,就构成了类似S[s+len]-S[s-1]<w的约束条件,只要所有约束条件符号相同,就可以构成一个差分约束系统。


想到这里,个人认为差分约束和最短路实质上不一样,只不过解法相同,是最短路的特例或者应用吧。好,那这题不同于poj3159,并不是求最大差距,怎么能转化成最短路呢?个人在此没有比较好的解释,只送上结论吧:

>=,求最小值,做最长路;

<=,求最大值,做最短路。

那么判断是否可以满足所有约束条件,就是这条最短路是否存在,这样就转变为了求负环,那么就是普通的spfa求负环了,判断条件就不多说了。注意这里用spfa求出的加上所有的约束条件也许并不能包含所有点,但是即使不处理剩余的点,那些剩余的点相当于没约束想取几就取几,不影响最后结果。


这里给出两种约束条件>和<,都要转化为<=才能进入系统。那么a[s1]+a[s2]+...a[s+len]>w就转变为S[s-1]-S[s+len]<=-w-1,a[s1]+a[s2]+...a[s+len]<w就转变为S[s+len]-S[s-1]<=w-1。这样在建立边的时候按照这种不等式建立。由于spfa是单源最短路,而这题没有明确的源点,所以构造超级源点。我说的也不怎么详细而且都是自己的看法,具体还是看上面大牛的博客吧= =。


#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <queue>
#include <stack>

using namespace std;

typedef long long LL;

const int N = 300;
const int INF = 0x3f3f3f3f;

int dis[N], head[N], out[N], cnt, n;
bool instack[N];

struct node
{
    int v, w, next;
}edge[N];

void add(int u, int v, int w)
{
    edge[cnt] = (struct node){v, w, head[u]};
    head[u] = cnt++;
}

void spfa()
{
    memset(instack, false, sizeof(instack));
    memset(out, 0, sizeof(out));
    stack<int>sta;
    for(int i = 0; i <= n+1; i++)
        dis[i] = INF;
    dis[n+1] = 0;
    instack[n+1] = true;
    sta.push(n+1);
    out[n+1]++;
    while(!sta.empty())
    {
        int u = sta.top();
        sta.pop();
        instack[u] = false;//记得还原!!
        out[u]++;
        if(out[u] > n+1)
        {
            printf("successful conspiracy\n");
            return;
        }
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if(dis[u]+edge[i].w<dis[v])
            {
                dis[v] = dis[u]+edge[i].w;
                if(!instack[v])
                {
                    instack[v] = true;
                    sta.push(v);
                }
            }
        }
    }
    printf("lamentable kingdom\n");
}

int main()
{
  //  freopen("in.txt", "r", stdin);
    int m, s, len, w;
    char str[5];
    while(~scanf("%d%d", &n, &m))
    {
        if(n == 0) break;
        cnt = 0;
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%s%d", &s, &len, str, &w);
            if(str[0] == 'g') add(s+len, s-1, -w-1);
            else if(str[0] == 'l') add(s-1, s+len, w-1);
        }
        for(int i = 0; i <= n; i++)//前面计算序列和的差时,由于得到中间的子序列值被减的序列和必须减一(s-1),所以这里必须把0也一同初始化。
            add(n+1, i, 0);
        spfa();
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值