Loading

[USACO05DEC] Layout G

算法

\(dis_i\) 表示第 \(i\) 头奶牛的坐标
题目转化为

对于 \(M_L\) 对数对 \((A_i, B_i) , A_i < B_i\) , 使得 \(dis_{B_i} - dis_{A_i} \leq D_i\)
对于 \(M_D\) 对数对 \((A_i, B_i) , A_i < B_i\) , 使得 \(dis_{B_i} - dis_{A_i} \geq D_i\)
对于 \((i, i + 1)\) , 有 \(dis_{i + 1} \geq dis_i, dis_i \leq dis_{i + 1}\)

两组约束不好处理, 考虑转化成一组约束

对于 \(M_L\) 对数对 \((A_i, B_i) , A_i < B_i\) , 使得 \(dis_{B_i} - dis_{A_i} \leq D_i\)
对于 \(M_D\) 对数对 \((A_i, B_i) , A_i < B_i\) , 使得 \(dis_{A_i} - dis_{B_i} \leq -D_i\)
对于 \((i, i + 1)\) , 有 \(dis_{i} \leq dis_{i + 1}\)

建立超级源点 \(0\) , 避免图不连通

考虑问题中三个结果

有解

先从 \(0\) 开始, 没有负环
再从 \(1\) 开始跑最短路

  • 存在最大值
    即为满足约束的最大距离
  • 不存在最大值
    分析满足这种情况的图长什么样
    \(1\)\(N\) 不连通, 则此时没有直接约束, 输出 \(-2\)

无解

先从 \(0\) 开始, 有负环则无解

代码

#include <bits/stdc++.h>
const int MAXM = 1e4 + 20;
const int MAXN = 1e3 + 20;

int N, ML, MD;
std::pair<int, int> Friend[MAXM], Enemy[MAXM];
int Dis_Friend[MAXM], Dis_Enemy[MAXM];

class Graph_Class
{
private:

public:
    struct Edge_Node
    {
        int to, w;
        int next;
    } Edge[(MAXM << 1) + MAXN];
    int Edge_Cnt = 0;
    int head[MAXN];
    void head_init() { for (int i = 0; i <= N; i++) head[i] = -1; }

    void addedge(int u, int v, int w)
    {
        Edge[++Edge_Cnt].to = v, Edge[Edge_Cnt].w = w;
        Edge[Edge_Cnt].next = head[u];
        head[u] = Edge_Cnt;
    }
} Graph;

class Sol_Class
{
private:
    std::queue<int> Q;
    int Neg[MAXN]; // 判断负环

    bool inq[MAXN];

    void init()
    {
        while(!Q.empty()) Q.pop();
        memset(Neg, 0, sizeof(Neg));
        memset(inq, false, sizeof(inq));
        memset(dis, 0x3f, sizeof(dis));
    }

public:
    int dis[MAXN];
    bool spfa(int Start)
    {
        init();
        Q.push(Start), Neg[Start]++, inq[Start] = true, dis[Start] = 0;

        while(!Q.empty())
        {
            int Now = Q.front();
            Q.pop();
            inq[Now] = false;
            for (int i = Graph.head[Now]; ~i; i = Graph.Edge[i].next) {
                int NowTo = Graph.Edge[i].to, NowW = Graph.Edge[i].w;
                if(dis[NowTo] > dis[Now] + NowW) {
                    dis[NowTo] = dis[Now] + NowW;
                    if(!inq[NowTo]) Q.push(NowTo), inq[NowTo] = true, Neg[NowTo]++;
                    if(Neg[NowTo] > N) return true;
                }
            }
        }

        return false;
    }
} Sol;

int main()
{
    scanf("%d %d %d", &N, &ML, &MD);
    for (int i = 1; i <= ML; i++)
        scanf("%d %d %d", &Friend[i].first, &Friend[i].second, &Dis_Friend[i]);
    for (int i = 1; i <= MD; i++)
        scanf("%d %d %d", &Enemy[i].first, &Enemy[i].second, &Dis_Enemy[i]);

    /*建边*/
    Graph.head_init();
    for (int i = 1; i <= ML; i++)
        Graph.addedge(Friend[i].first, Friend[i].second, Dis_Friend[i]);
    for (int i = 1; i <= MD; i++)
        Graph.addedge(Enemy[i].second, Enemy[i].first, -Dis_Enemy[i]);
    for (int i = 1; i <= N - 1; i++)
        Graph.addedge(i + 1, i, 0);
    /*超级源点*/
    for (int i = 1; i <= N; i++)
        Graph.addedge(0, i, 0);

    /*判断是否有解*/
    if(Sol.spfa(0)) printf("-1");
    else {
        Sol.spfa(1);
        if(Sol.dis[N] == 0x3f3f3f3f)
            printf("-2");
        else printf("%d", Sol.dis[N]);
    }

    return 0;
}

/*
4 2 1
1 3 10
2 4 20
2 3 3

27
*/

总结

对于这样的一类题:

  1. 转化为单组约束
  2. 判断是否有解(先要有解才能继续讨论), 使用超级源点解决连通性问题
  3. 判断是否有约束(这个不是所有题都要用)
posted @ 2024-11-09 16:16  Yorg  阅读(21)  评论(0)    收藏  举报