POJ3159 Candies

Candies
Time Limit: 1500MS
Memory Limit: 131072K
Total Submissions: 35568
Accepted: 9998

Description

During the kindergarten days, flymouse was the monitor of his class. Occasionally the head-teacher brought the kids of flymouse’s class a large bag of candies and had flymouse distribute them. All the kids loved candies very much and often compared the numbers of candies they got with others. A kid A could had the idea that though it might be the case that another kid B was better than him in some aspect and therefore had a reason for deserving more candies than he did, he should never get a certain number of candies fewer than B did no matter how many candies he actually got, otherwise he would feel dissatisfied and go to the head-teacher to complain about flymouse’s biased distribution.

snoopy shared class with flymouse at that time. flymouse always compared the number of his candies with that of snoopy’s. He wanted to make the difference between the numbers as large as possible while keeping every kid satisfied. Now he had just got another bag of candies from the head-teacher, what was the largest difference he could make out of it?

Input

The input contains a single test cases. The test cases starts with a line with two integers N and M not exceeding 30 000 and 150 000 respectively. N is the number of kids in the class and the kids were numbered 1 through N. snoopy and flymouse were always numbered 1 and N. Then follow M lines each holding three integers AB and c in order, meaning that kid A believed that kid B should never get over c candies more than he did.

Output

Output one line with only the largest difference desired. The difference is guaranteed to be finite.

Sample Input

2 2
1 2 5
2 1 4

Sample Output

5

Hint

32-bit signed integer type is capable of doing all arithmetic.

Source


题意:有一个幼儿园,里面每个小朋友都很傲娇,每次发糖果的时候,一定不能比另外一些小朋友少特定数量的糖果,不然的话小朋友就会去找班主任告状说分配不均。snoopy是这个班的班长,他总是和一个叫snoopy的小朋友比糖果,现在问题来了。给定一些糖果的分配关系。问flymouse和snoopy最大的糖果差距是多少。

解法:一开始看到这道题一点不会。果然没做过题的人是不会这种高级的东西的,这个东西的知识点叫“差分约束”。可以用最短路算法解决,下面是一些资料。

一、何为差分约束系统:
差分约束系统(system of difference constraints),是求解关于一组变数的特殊不等式组之方法。如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统(system of difference constraints)。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
通俗一点地说,差分约束系统就是一些不等式的组,而我们的目标是通过给定的约束不等式组求出最大值或者最小值或者差分约束系统是否有解。

比如:


二、差分约束系统的求解:

差分约束系统可以转化为图论来解决,对应于上面的不等式组,如果要求出x3-x0的最大值的话,叠加不等式可以推导出x3-x0<=7,最大值即为7,我们可以通过建立一个图,包含6个顶点,对每个xj-xi<=bk,建立一条i到j的有向边,权值为bk。通过求出这个图的x0到x3的最短路可以知道也为7,这是巧合吗?并不是。

之所以差分约束系统可以通过图论的最短路来解,是因为xj-xi<=bk,会发现它类似最短路中的三角不等式d[v] <=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]。而求取最大值的过程类似于最短路算法中的松弛过程。

三角不等式:(在此引用大牛的博客)
B - A <= c     (1)
C - B <= a     (2)
C - A <= b     (3)

 如果要求C-A的最大值,可以知道max(C-A)= min(b,a+c),而这正对应了下图中C到A的最短路。 


因此,对三角不等式加以推广,变量n个,不等式m个,要求xn-x1的最大值,便就是求取建图后的最短路。
同样地,如果要求取差分约束系统中xn-x1的最小值,便是求取建图后的最长路。最长路可以通过spfa求出来,只需要改下松弛的方向即可,即if(d[v] < d[u] + dist(u,v)) d[v] = d[u] + dist(u,v)。当然我们可以把图中所有的边权取负,求取最短路,两者是等价的。

以上内容转载自:http://https://blog.csdn.net/consciousman/article/details/53812818

接下来就可以做了。那么这里因为数据量较大,所以我们需要用dijkstra+heap算法优化。

由于数据卡vector会超时,于是我们需要用数组实现邻接表。

这里再贴一个很好的讲解:

具体的思路是,在建立邻接表时,记录的不是点而是边,对于每一个点所对应的邻接表都是以的形式存储的,也就是说先添加的边在遍历时后取出,除此以外,所有的边用一个结构体数组存储起来,每条边对应的索引就是其编号,在建立邻接表时,表中存放的实质是边的编号,在遍历时先获得编号,在放回结构体数组中获得相应的边的数据。

下面用具体代码解释一下:

int top=0;//用来确定当前边的编号
int head[MAX_N]=-1;//MAX_N是点的最大数目,该数组用来存放每一个点在建立邻接表时,栈顶的边的编号
struct Edge//边的结构体
{
    int v;//边所连接的点
    int next;//在栈中的底下一条边的编号
}e[MAX_E];//MAX_E是边的最大数目,当图为无向图时,对一条边的两个端点建立邻接表时,
          //均会记录该边,因此,同一条边会被记录两次
void addEdge(int u,int v)//在u点的邻接表中加入一条边,也就是在栈顶加入一条边
{
    e[top].v=v;
    e[top].next=head[u];//在u顶点的邻接表这个栈的顶部加入一条边(头插法)
    head[u]=top++;//top为加入边的编号,加入后要更新head,使得head记录邻接表栈顶边的编号
}
void bfs()//遍历邻接表
{
    for(int i=0;i<n;i++)//遍历所有点
    {
        for(int j=head[i];j!=-1;j=e[j].next)//j=head[i]:获取当前点邻接表的栈顶的边的编号
        {                                   //j=e[j].next:获取栈中底下一条边的编号,j==-1表示遍历完
            //操作
        }
    }
}

以上内容转载自:https://blog.csdn.net/zoucharming/article/details/41351389

那么剩下的其实就简单了,读入每个关系后,a和b连一条权值为c的边(注意是有向图)

然后加边dijkstra输出dist[n]即可

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <string>
#include <iostream>
#include <stack>
#include <math.h>
#include <queue>
#include <vector>
#include <map>
#include <climits>
using namespace std;
typedef long long ll;
const int maxn = 3e4 + 10;
const int maxm = 2e6;
const int inf = 0x01010101;
map<string,int> num;
struct edge//存储每一条边
{
    int to,cost,next;//分布表示指向的结点,权值以及邻接表中下一条边的编号
    edge(int a,int b,int c)
    {
        to = a;
        cost = b;
        next = c;
    }
    edge() {}
};
struct node
{
    int v,d;
    node(int _v,int _d)
    {
        v = _v;
        d = _d;
    }
    bool operator < (const node &r) const
    {
        return d > r.d;//使得优先队列按权值从小到大出队
    }
};
edge eg[maxm];
int total;//边的个数
int n,m;
int head[maxn];
void addedge(int a,int b,int c)
{
    eg[total] = edge(b,c,head[a]);//初始化该边的终结点,权值,和邻接表中下一条边的编号
    head[a] = total;//头插入
    total++;//边数++

}
int dist[maxn];//存储最短距离
bool used[maxn];
void dijkstra()
{
    memset(dist,1,sizeof(dist));
    fill(dist + 1,dist + n + 1,inf);
    dist[1] = 0;
    priority_queue<node> Q;
    Q.push(node(1,0));
    while(Q.size())
    {
        node temp = Q.top();
        Q.pop();
        int v = temp.v;
        if(dist[v] < temp.d) continue;
        used[v] = 1;
        for(int i = head[v]; i != -1; i = eg[i].next)
        {
            if(!used[eg[i].to] && dist[eg[i].to] > dist[v] + eg[i].cost)
            {
                dist[eg[i].to] = dist[v] + eg[i].cost;
                Q.push(node(eg[i].to,dist[eg[i].to]));
            }
        }
    }

}
int main()
{
    // freopen("out.txt","w",stdout);

    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        addedge(a,b,c);
    }
    dijkstra();
    printf("%d\n",dist[n]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值