POJ 1201 浅谈拟构限制SPFA求解差分约束系统

这里写图片描述
世界真的很大
一些条件限制的题目可以用SPFA来转化成差分约束系统
能想到这个的却是不错,但是千万不要考虑漏了
差分约束在某些题目中除了题目条件还有一些隐藏条件
如果不处理这些隐藏条件,差分约束系统跑出来的结果可能会自相矛盾
这道题就是如此

看题先:

description:

给定n个闭合的整数间隔[ai,bi]和n个整数c1,…,cn。
写一个程序:
从标准输入读取间隔数,它们的终点和整数c1,…,cn,
对于每个i = 1,2,…,n,计算具有至少具有间隔[ai,bi]的公共元素的整数集合Z的最小大小,
将答案写入标准输出。

input:

输入的第一行包含整数n(1 <= n <= 50000) - 间隔数。 以下n行描述间隔。 输入的第(i + 1)行包含由单个空格分开的三个整数ai,bi和ci,并且使得0 <= ai <= bi <= 50000和1 <= ci <= bi-ai + 1。

output:

对于每个i = 1,2,…,n,输出包含恰好等于组Z的至少ci元素的最小尺寸的整数,其中间距为[ai,bi]。

首先一个套路性的东西就是[L,R]数字个数可以转化成[1,R]的个数减去[1,L-1]的个数,倒不是说一定是这样,但起码看到应该有这样的思路
然后每一个条件限制就变成了:[1,R]-[1,L-1] > = ci
有n组这样的式子,差分约束系统还是比较显然的
然后就按差分约束系统建边,由于范围不会超过5e5,直接用L,R来当编号就行了,直接用SPFA跑最长路,dis[maxn]就是答案
当然不会这么简单,这样跑出来样例的结果是5,为什么会这样呢?
然后把每一个数的dis输出来,dis[7]是4,然而dis[8]是1。。
自相矛盾了
之所以会这样,是因为我们直接按照题目所给的信息建边,而在题目中,7和8没有给出的关系,但是[1,8]怎么着也是应该大于[1,7]的
但是在差分约束系统中我们没有加入这个限制
加入之后,我们就相当于保证了8比7大,看似毫无问题,交了一波恭喜WA

为什么会这样呢?理论上来讲不该有问题啊,我们保证了小区间一定比大区间小啊?
当自己的理解已经到瓶颈时,优先确认一波代码有没有什么大错误,恰恰是没有的。那么只能说,对题意理解不深
再看一下题
我们要求的是整数集合


什么是集合?
集合中元素貌似。。。不可重吧?
我们保证了[1,8]比[1,7]大,但我们没有保证大多少,[1,8]比[1,7]大最多只能大1,因为8这个数在集合里可以选且只能选一次
即dis[8]-dis[7] < = 1
而我们之前的差分约束系统没有考虑这个条件
交了一波
稳稳AC

总结一下,做差分约束问题时,由于方法大家都知道,不就是SPFA嘛。出题人要考你,只能考条件的转化。
所以差分约束的题,条件肯定是十分重要的,认真分析有没有隐藏的限制条件

完整代码:

#include<stdio.h>
#include<cstring>
#include<queue>
using namespace std;

const int INF=0x3f3f3f3f;

struct edge
{
    int v,last,w;
}ed[1000010];

queue <int> state;

int n,num=0,S=INF,maxn=0,dis[500010],se[500010],mrk[500050],in[500050],head[500050];

void add(int u,int v,int w)
{
    num++;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].last=head[u];
    head[u]=num;
}

void SPFA()
{
    for(int i=1;i<=maxn;i++) dis[i]=-INF;
    state.push(S);
    dis[S]=0,se[S]=1;
    while(!state.empty())
    {

        int u=state.front();
        state.pop(),se[u]=0;
        for(int i=head[u];i;i=ed[i].last)
        {
            int v=ed[i].v;
            if(dis[v]<dis[u]+ed[i].w)
            {
                dis[v]=dis[u]+ed[i].w;
                if(!se[v])
                {
                    se[v]=1;
                    state.push(v);
                }
            }
        }
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u-1,v,w);
        S=min(S,u-1),maxn=max(maxn,v);
    }
    for(int i=S;i<maxn;i++)
        add(i,i+1,0),add(i+1,i,-1);
    SPFA();
    printf("%d\n",dis[maxn]);
    return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/

嗯,就是这样

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值