新英雄 二分答案 差分约束

新英雄

问题描述

老张也准备沉迷于lol不能自拔。为了表示自己的诚意,老张设计了一个新英雄。这个新英雄的大招非常强势,在追人的时候能体现非常强的优势。假设召唤师峡谷是一个有n个节点,m条单向边的图。对于每一个节点x,可以把所有以x为终点的边的权值减少d(-10000<=d<=10000),同时把所有以x为起点的边的权值加上d。要让所有边的权值的最小值最大。当然,边的权值不能为零或负,因为这不符合召唤师峡谷的物理规律。

输入格式

有多组数据,对于每一组数据:
第一行为两个整数n,m
接下来m行,每一行三个整数a,b,c 表示从a到b有一条长度为c的道路

输出格式

对于每一个数据块输出文件仅有一行:
如果答案有且仅有一个解,输出最短道路的最大可能值
如果答案具有任意性,即有多解,输出”Infinite”
如果无解,输出”No Solution”

样例输入

2 1
1 2 10
2 1
1 2 -10
3 3
1 2 4
2 3 2
3 1 5
4 5
2 3 4
4 2 5
3 4 2
3 1 0
1 2 -1

样例输出

Infinite
Infinite
3
1

数据规模

n≤500,m≤2700,-10000<=d<=10000每条道路的长度保证不超过10000


“最小值最大”,二分答案标志。显然,如果有解而且有有限解,那么答案一定在[1,10000]内。现在考虑如何验证答案。

设当前检验的答案为 ans ,在 p 号点操作的总权值为d[p],那么对于每一条边都满足以下不等式:

ansd[x]d[y]+len[x][y]

移项后得到差分约束的标准形式:
d[y]d[x]+len[x][y]ans

所以如果当前答案都能满足所有这样的不等式,那就是合法的,反之就不是。也就是说,这样建图后,如果有负权回路就是不合法的,反之就是合法的。所以采用SPFA统计入队次数的方法。

那么什么情况下无解呢?如果原图中就有负环就无解。
什么情况下有多解呢?验证10001或更大的一个数,如果合法就说明有多解。

需要注意,每组数据先判断无解和无数组解的情况会比较好,特别是先判是否无解,因为如果无解,那么二分答案时,每次SPFA都要跑很多次使得入队次数达到 (N+1)


代码:

#include<stdio.h>
#include<cstring>
#define MAXN 505
#define MAXM 6005
using namespace std;

int N,M;

int en[MAXM],nex[MAXM],las[MAXN],len[MAXM],tot;
void Add(int x,int y,int z)
{
    en[++tot]=y;
    nex[tot]=las[x];
    las[x]=tot;
    len[tot]=z;
}

int Cnt[MAXN],Dis[MAXN];
bool mark[MAXN];
int Q[1000005],head,tail;

bool SPFA(int s,int k)
{
    memset(Cnt,0,sizeof(Cnt));
    memset(Dis,60,sizeof(Dis));
    memset(mark,false,sizeof(mark));

    Dis[0]=0;
    head=1;
    tail=0;

    int i,x,y;
    Q[++tail]=s;
    while(head<=tail)
    {
        x=Q[head];head++;mark[x]=false;
        for(i=las[x];i;i=nex[i])
        {
            y=en[i];
            if(Dis[y]>Dis[x]+len[i]-k)
            {
                Dis[y]=Dis[x]+len[i]-k;
                if(!mark[y])
                {
                    mark[y]=true;
                    Cnt[y]++;
                    if(Cnt[y]>N)return false;
                    Q[++tail]=y;
                }
            }
        }
    }
    return true;
}

void Init()
{
    memset(las,0,sizeof(las));
    tot=0;
}

int main()
{
    int i,x,y,z;

    while(scanf("%d%d",&N,&M)!=EOF)
    {
        Init();

        for(i=1;i<=M;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            Add(x,y,z);
        }

        for(i=1;i<=N;i++)Add(0,i,0);

        if(!SPFA(0,1))
        {
            puts("No Solution");
            continue;
        }
        if(SPFA(0,10001))
        {
            puts("Infinite");
            continue;
        }

        int L=1,R=10000,mid;
        while(L<=R)
        {
            mid=L+R>>1;
            if(SPFA(0,mid))L=mid+1;
            else R=mid-1;
        }
        printf("%d\n",R);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值