AcWing 1169. 糖果(spfa负环做差分约束问题)
将题中所给条件转化为不等式(左小右大),不等式等价于建图时的边,再找到一个合适的源点(一般是建立0点为超级源点),注意合适是值这个点能到达所有其他点,之后将所有给出的边加入图中,之后将每个点都和超级源点都建立连接,之后跑spfa,如果存在正环的情况下,说明不能找到合适的值,否则就更新出每个点的最大路径。最大路径值之和就是题中所求最小糖果数
如果求最小值,应该用最长路,如果求最大值,应该用最短路
大佬详解这类题做法
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 3 * N;
int h[N], ne[M], e[M], w[M], idx;
int n, m;
int dist[N];
int q[N];
int cnt[N];
bool st[N];
void add(int a, int b, int c){
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx ++ ;
}
bool spfa(){
memset(dist, -0x3f, sizeof dist);
int hh = 0, tt = 1;
q[0] = 0; //超级源点入栈
dist[0] = 0;
st[0] = true;
while(hh != tt){
int t = q[ -- tt];
st[t] = false;
for(int i = h[t]; ~i; i = ne[i]){
int j = e[i];
if(dist[j] < dist[t] + w[i]){ //要找的是正环,所以要找最长路
dist[j] = dist[t] + w[i];
cnt[j] = cnt[t] + 1;
//存在正环的情况下,代表所以小朋友的情况不可能都满足,所以返回false
if(cnt[j] >= n + 1) return false; //算上超级源点总共有n+1个点,
if(!st[j]){
q[tt ++ ] = j;
st[j] = true;
}
}
}
}
return true;
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
while(m -- ){
int a, b, x;
cin>>x>>a>>b;
//下面的转化很关键,就是将题意转化为建图
//因为要用较小的更新较大的 ,所以建图时边的起点是较小的一方
if(x == 1) add(a, b, 0), add(b, a, 0);
else if(x == 2) add(a, b, 1);
else if(x == 3) add(b, a, 0);
else if(x == 4) add(b, a, 1);
else add(a, b, 0);
}
for(int i = 1; i <= n; i ++ ) add(0, i, 1); //超级源点距离每个点都要建立一条边,权重为1,因为要用源点更新这些边
if(!spfa()) cout<<"-1"<<endl;
else{
long long res = 0;
for(int i = 1; i <= n; i ++ ) res += dist[i];
cout<<res<<endl;
}
return 0;
}