POJ 3169 Layout (差分约束)

题目大意:一条直线上有n个点,现给出ml个条件(i,j,w),描述第i个点到第j个点之间的距离小于等于w,和md个条件(i,j,w),描述第i个点到第j个点之间的距离小于等于w。现在请问第1个点到第n个点之间的距离最大是多少?如果是无限大,输出-2;如果无解,输出-1。
数据范围:n<=1000 ml,md<=10000,w<=1000000。
分析:首先形式化问题,设f(x)为第x个点所对应的位置坐标。那么题目转化为:
已知若干个形如f(j)-f(i)<=w的不等式,求f(n)-f(1)的最大值。
求不等式组的解集很明显是一个差分约束问题。
f(j)-f(i)<=w,变形以后变成f(j)<=f(i)+w。因为这个式子类似求最短路中的松弛操作,所以用图论的想法来看待这个式子,那么就可以发现:i到j有一条权值为w的边,并且i不能松弛j。逗号前面的解释了怎样建图,逗号后面的说明了在求f(n)-f(1)的最大值的时候,在对应的图里面要求从1到n的最短路,这个可以用spfa跑。
如果图中有负权环,那么就无解(spfa可以判断),如果1到n不连通,就是无限大(可以自己尝试证明一下)。

下面是代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 999999999
using namespace std;
struct node{
    int v,next,w;
}edge[30000];
int head[30000],n,ml,md,cnt,dis[1010],num[1010];
bool vis[1010];
queue<int>q;
void addedge(int u,int v,int w)
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
bool spfa()
{
    for(int i=2;i<=n;++i)
        dis[i]=inf;
    vis[1]=1;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v,w=edge[i].w;
            if(dis[u]+w<dis[v])
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    if(num[v]>n)return 1;//判断负权环路
                    num[v]++;
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    return 0;
}
int main()
{
    memset(head,-1,sizeof head);
    scanf("%d%d%d",&n,&ml,&md);
    for(int i=0;i<ml;++i)//f(u)-f(v)<=w -> f(u)<=f(v)+w
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
    }
    for(int i=0;i<md;++i)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);//f(u)-f(v)>=w -> f(v)<=f(u)-w
        addedge(v,u,-w);
    }
    for(int i=1;i<n;++i)//题目有个隐藏条件:f(i+1)>f(i)
        addedge(i+1,i,0);
    if(spfa())printf("-1\n");
    else if(dis[n]==inf)printf("-2\n");//不连通
    else printf("%d\n",dis[n]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值