[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
*/
总结
对于这样的一类题:
- 转化为单组约束
- 判断是否有解(先要有解才能继续讨论), 使用超级源点解决连通性问题
- 判断是否有约束(这个不是所有题都要用)

浙公网安备 33010602011771号