[BZOJ 2330][SCOI 2011]糖果(差分约束系统)

142 篇文章 0 订阅
98 篇文章 0 订阅

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=2330

思路

刚开始看这题就想到用SPFA之类的求求最短路啥的,但是没有很清晰的思路,翻了题解才发现这是一个很裸的差分约束系统的题(不会差分约束系统的话比赛时就要GG啊)。

对于每个限制条件,在差分约束系统中我们都能把它们表示成 xy<=a 的形式,从 y x连边权为 a 的边,而对于xy<a的形式也可转化,因为题目中的数字都是整数,答案也必须是整数,因此 xy<a 等价于 xy<=a1
于是我们就可以对题目数据中给出的每个限制条件建立一个图了,我们需要做的就是用SPFA求出这个图的最长路,但是这个最长路也是非常特殊的,对于任意点 i 而言,除了i以外的其他点都能作为以这个点作为终点的最长路的起点(为了做到这一点,可以建立超级源点,向每个点都连边,注意这些边的边权必须为1,因为要保证每个人都能得到糖果),而答案则是每个点作为终点的最长路径的长度之和。

为什么是这样呢?注意到一条指向点 i 的路径就是形如x+a1<=y+a2<=z+a3<=...<=i的一串不等式,不等式的最左边的 x 就是某条终点为i的路径的起点,为了满足这个不等式且 i 尽量小,显然所有等号必须取等,又因为每个孩子都得得到糖果,因此初始的x必须是取最小值1, i 必须满足所有这样的等式,因此必须取超级源到i的最长路,这就是 i 得到的糖果个数,求和即可得到答案。

另外就是特判的问题,差分约束系统的特判要讲究技巧,由于上面我们要求的是最长路,因此若图中有正环就无解(若题目要求最短路,则有负环就无解),这个可以通过SPFA实现,而在输入数据时,形如x<y这样的不等式,若 x=y 则无解,特判即可。

代码

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

#define MAXE 210000 //!!!!!!
#define MAXV 110000

using namespace std;

typedef long long int LL;

int n,k;

struct edge
{
    int u,v,w,next;
}edges[MAXE];

int head[MAXV],nCount=0;

void AddEdge(int U,int V,int W)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].w=W;
    edges[nCount].next=head[U];
    head[U]=nCount; //!!!!!
}

int dist[MAXV],vis[MAXE];
bool inQueue[MAXV];
queue<int>q;

bool SPFA()
{
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        inQueue[u]=false;
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(dist[u]+edges[p].w>dist[v])
            {
                dist[v]=dist[u]+edges[p].w;
                vis[p]++;
                if(vis[p]>n) return false; //某条边的访问次数大于n,则说明出现了正环,有正环就不存在最长路,无解
                if(!inQueue[v])
                {
                    inQueue[v]=true;
                    q.push(v);
                }
            }
        }
    }
    return true;
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&k);
    for(int i=1;i<=k;i++)
    {
        int op,u,v;
        scanf("%d%d%d",&op,&u,&v);
        if(op==1) //u==v
            AddEdge(u,v,0),AddEdge(v,u,0);
        else if(op==2) //u<v
        {
            if(u==v) //特判
            {
                printf("-1");
                return 0; //!!!!!!
            }
            AddEdge(u,v,1);
        }
        else if(op==3) //u>=v
            AddEdge(v,u,0);
        else if(op==4) //u>v
        {
            if(u==v) //特判
            {
                printf("-1");
                return 0;
            }
            AddEdge(v,u,1);
        }
        else //u<=v
            AddEdge(u,v,0);
    }
    for(int i=1;i<=n;i++)
    {
        dist[i]=1; //每个人必须都有糖果
        inQueue[i]=true;
        q.push(i);
    }
    LL ans=0;
    if(!SPFA())
    {
        printf("-1\n");
        return 0;
    }
    for(int i=1;i<=n;i++) ans+=dist[i];
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值