zoukankan      html  css  js  c++  java
  • [Luogu1993] 小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。


    差分约束的裸题, 但是普通的寻找负环的算法会TLE。

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    #define reg register 
    inline int read() {
        int res = 0;char ch=getchar();bool fu=0;
        while(!isdigit(ch)) {if(ch=='-')fu=1;ch=getchar();}
        while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
        return fu?-res:res;
    }
    #define N 10005
    int n, m;
    int S;
    struct edge {
        int nxt, to, val;    
    }ed[N*2];
    int head[N], cnt;
    inline void add(int x, int y, int z)
    {
        ed[++cnt] = (edge){head[x], y, z};
        head[x] = cnt;
    }
    int vis[N];
    int dis[N];
    bool ex[N];
    
    inline bool spfa()
    {
        memset(dis, 0x3f, sizeof dis);
        queue <int> q;
        dis[S] = 0;
        q.push(S);
        while(!q.empty())
        {
            int x = q.front();q.pop();
            vis[x] ++;
            ex[x] = 0;
            if (vis[x] >= n) return 0;
            for (reg int i = head[x] ; i ; i = ed[i].nxt)
            {
                int to = ed[i].to;
                if (dis[to] > dis[x] + ed[i].val)
                {
                    dis[to] = dis[x] + ed[i].val;
                    if (!ex[to]) ex[to] = 1, q.push(to);
                }
            }
        }
        return 1;
    }
    
    int main()
    {
        n = read(), m = read();
        for (reg int i = 1 ; i <= m ; i ++)
        {
            int opt = read();
            if (opt == 3) {
                int x = read(), y = read();
                add(x, y, 0), add(y, x, 0);
            } else if (opt == 2) {
                int x = read(), y = read(), z = read();
                add(y, x, z);
            } else {
                int x = read(), y = read(), z = read();
                add(x, y, -z);
            }
        }
        S = n + 1;
        for (reg int i = 1 ; i <= n ; i ++) add(S, i, 0);
        puts(spfa() ? "Yes" : "No");
        return 0;
    }
    朴素SPFA

    只有60分。

    用$bfs$实现$spfa$,时间复杂度$large O(KN)$,$K$为节点平均入队次数。

    这是规定了搜索的深度,真的是期望!!!!

    这个快死了的算法随便卡...

    寻找负环还可以用$dfs$实现。

    对一个节点开始搜索,如果又经过了已近经过的点,那就是存在负环,就是一点在一条路径上多次出现。

    这已经比之前优秀多了, 但还有更优秀的做法。

    寻找负环,我们只用找到一条权值和为负数的一个环就行了,那么我们将$dis$数组初始化的时候设为0, 这样只会拓展与这个点相连的负权边。

    保证权值和始终为fu,剪掉了大部分枝。

    可过。


    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    #define reg register 
    inline int read() {
        int res = 0;char ch=getchar();bool fu=0;
        while(!isdigit(ch)) {if(ch=='-')fu=1;ch=getchar();}
        while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
        return fu?-res:res;
    }
    #define N 10005
    int n, m;
    int S;
    struct edge {
        int nxt, to, val;    
    }ed[N*2];
    int head[N], cnt;
    inline void add(int x, int y, int z)
    {
        ed[++cnt] = (edge){head[x], y, z};
        head[x] = cnt;
    }
    int vis[N];
    int dis[N];
    bool ex[N];
    bool Find;
    
    void SPFA(int x)
    {
        ex[x] = 1;
        for (reg int i = head[x] ; i ; i = ed[i].nxt)
        {
            int to = ed[i].to;
            if (dis[to] > dis[x] + ed[i].val)
            {
                dis[to] = dis[x] + ed[i].val;
                if (ex[to] or Find) {Find = 1;return ;}
                SPFA(to); 
            }
        }
        ex[x] = 0;
    }
    
    int main()
    {
        n = read(), m = read();
        for (reg int i = 1 ; i <= m ; i ++)
        {
            int opt = read();
            if (opt == 3) {
                int x = read(), y = read();
                add(x, y, 0), add(y, x, 0);
            } else if (opt == 2) {
                int x = read(), y = read(), z = read();
                add(y, x, z);
            } else {
                int x = read(), y = read(), z = read();
                add(x, y, -z);
            }
        }
        S = n + 1;
        for (reg int i = 1 ; i <= n ; i ++) add(S, i, 0);
        memset(dis, 0, sizeof dis);
        for (reg int i = 1 ; i <= n ; i ++) {SPFA(i);if(Find)break;}
        puts(Find ? "No" : "Yes");
        return 0;
    }
  • 相关阅读:
    English 2
    速算24点
    心理学1
    从微服务到函数式编程
    034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述
    033 01 Android 零基础入门 01 Java基础语法 03 Java运算符 13 运算符和表达式知识点总结
    032 01 Android 零基础入门 01 Java基础语法 03 Java运算符 12 运算符和if-else条件语句的综合案例——闰年问题
    031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级
    030 01 Android 零基础入门 01 Java基础语法 03 Java运算符 10 条件运算符
    029 01 Android 零基础入门 01 Java基础语法 03 Java运算符 09 逻辑“非”运算符
  • 原文地址:https://www.cnblogs.com/BriMon/p/9614559.html
Copyright © 2011-2022 走看看