P1993 小K的农场(差分约束)

题目描述

小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述:

农场a比农场b至少多种植了c个单位的作物, 农场a比农场b至多多种植了c个单位的作物, 农场a与农场b种植的作物数一样多。
但是,由于小K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。

输入格式 第一行包括两个整数 n 和 m,分别表示农场数目和小 K 记忆中的信息数目。

接下来 m 行:

如果每行的第一个数是 1,接下来有 3 个整数 a,b,c,表示农场 a 比农场 b 至少多种植了 c 个单位的作物。

如果每行的第一个数是 2,接下来有 3 个整数 a,b,c,表示农场 a 比农场 b 至多多种植了 c 个单位的作物。如果每行的第一个数是
3,接下来有 2 个整数 a,b,表示农场 a 种植的的数量和 b 一样多。

输出格式 如果存在某种情况与小 K 的记忆吻合,输出“Yes”,否则输出“No”。

输入输出样例

输入 #1 复制
3 3
3 1 2
1 1 3 1
2 2 3 2
输出 #1 复制
Yes

说明/提示 对于 100% 的数据保证:1 ≤ n,m,a,b,c ≤ 10000。

思路

  • 这题就是差分约束,但是是我这蒟蒻懵了半天,一直蒙在 “为什么有时候要跑最短路、有时候要跑最长路,都跑 最短路径不行吗?”,后来才逐渐明白了点,跑最短路的时候是为了找 (题目所需要)最大的答案;跑最长路径是为了找(题目所需要的)的小答案。。。。。。但是,我们总是觉的很怪异,为啥 跑最长路的出来的结果是 最小答案呢???,,
  • 我其实我们可以考虑 在跑最长的的时候, 我们要把所有的差分约束条件变成 “ >= 的形式, 我们可以这样想 假如 a >= 10 ,那么 a的最小值就是 10,当把我们 所有的约束条件变成 >= 形式的时候,当我们Spfa跑完最长路经的时候,在图中任意的节点 u、v,对应边权为w ,一定满足dis[ v ] >= dis[ u] + w = 某个值. … …所以同理得出的是最小答案
  • 这样想了之后,我们可以 推测出为什么 跑 最短路 出来的结果 为什么是最大答案了。。

还有最后一点做题时要先弄明白:

  • 题目让我们求最大 ans 的时候 ,我们要把所有的等式变成 <= 的形式,并且跑最短路就行了,考虑这个时候点最短路建边 ,,,例如 :a - b <= 10 =》. a <= b+10, 我们可以 建一条权值为 10,b -> a 的边 ,
  • 题目让我们求最大 ans 的时候 ,我们要把所有的等式变成 >= 的形式,并且跑最长路就行了,考虑这个时候点最短路建边 ,,,例如 :a - b >= 10 =》. a >= b+10, 我们可以 建一条权值为 10,b -> a 的边 …照着公式建边就行了。。

题解一(80分不开02优化)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x7ffffff
const int maxn = 100005;
const int maxm = 200005;

struct Edge
{
    int v,w,next;
} edge[maxm];
int head[maxm];
int dis[maxm];
int use[maxm];
int cnt[maxm];
int k = 0;
int n,m;

void Add(int u, int v, int w)
{
    edge[++ k] = (Edge){ v, w, head[u]}; head[u] = k;
}

bool Spfa(int s)
{
    for(int i = 0; i <= n; i ++)
        dis[i] = INF, use[i] = 0, cnt[i] = 0;
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    int u,v,w;

    while(! q.empty())
    {
        u = q.front(); q.pop();
        use[u] = 0;

        cnt[u] ++;
        if(cnt[u] > n + 1 || dis[s] < 0) return false;

        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].v;
            w = edge[i].w;
            if(dis[v] > dis[u] + w)
            {
                dis[v] = dis[u] + w;
                if(! use[v])
                {
                    q.push(v);
                    use[v] = 1;
                }
            }
        }
    }
    return true;
}


void init()
{
    k = 0;
    memset(head, -1, sizeof(head));
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0);
    //freopen("T.txt","r",stdin);
    cin >> n >> m;
    init();
    int tp;
    int u,v,w;
    for(int i = 1; i <= m; i ++)
    {
        cin >> tp;
        if(tp == 1)
        {
            cin >> u >> v >> w;
            Add(u, v, -w);
            // u - v >= w
            // v <= u - w
        }
        else if(tp == 2)
        {
            cin >> u >> v >> w;
            // u - v <= w;
            // u <= v + w;
            Add(v, u, w);
        }
        else
        {
            cin >> u >> v;
            Add(u, v, 0);
            Add(v, u, 0);
        }
    }
    for(int i = 1; i <= n; i ++)
        Add(0, i, 0);
    if(Spfa(0))
        cout << "Yes\n";
    else
        cout << "No\n";

    return 0;
}

题解二(Spfa dfs优化版)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 100005;
const int maxm = 200005;
struct Edge
{
    int v,w,next;
} edge[maxm];
int head[maxn],used[maxn];
int dis[maxn];
int n,m;
int k = 0;

void Add(int u, int v, int w)
{
    edge[++ k] = (Edge){ v, w, head[u]}; head[u] = k;
}

bool Spfa(int now)
{
    used[now] = true;		//做标记 标记节点 now 已经使用过了,在dfs中如果 若是再次走到这个 带标记的now 则代表图中有环
    for(int i = head[now]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        int w = edge[i].w;
        if(dis[v] > dis[now] + w)
        {
            if(used[v]) return false;	//若是再次走到这个 带标记的v 则代表图中有环
            dis[v] = dis[now] + w;
            if(! Spfa(v)) return false;
        }
    }
    used[now] = false;
    return true;
}


void init()
{
    for(int i = 0; i <= n; i ++)
        head[i] = -1, dis[i] = INF;
    dis[0] = 0;
    k = 0;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0);
    //freopen("T.txt","r",stdin);
    cin >> m >> n;
    init();
    int tp;
    for(int i = 1; i <= n; i ++)
    {
        cin >> tp;
        int a,b,c;
        if(tp == 1)
        {
            cin >> a >> b >> c;
            // a - b >= c
            // b <= a - c
            Add(a, b, -c);
        }
        else if(tp == 2)
        {
            cin >> a >> b >> c;
            // a - b <= c;
            // a <= b + c;
            Add(b, a, c);

        }
        else
        {
            cin >> a >> b;
            Add(a, b, 0);
            Add(b, a, 0);
        }
    }
    for(int i = 1; i <= n; i ++)
        Add(0, i, 0);

    if(Spfa(0)) cout << "Yes\n";
    else cout << "No\n";

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值