来自算法竞赛进阶指南的讲解
x
−
y
<
=
k
x-y<=k
x−y<=k 形式可以转化为
x
<
=
y
+
k
x <= y + k
x<=y+k,
x
x
x 会变得尽量小,和最短路一样,
y
y
y 可以用
k
k
k 去更新
x
x
x,因此建边
add(y, x, k);
而 x − y > = k x-y>=k x−y>=k,可以写成 x > = y + k x>=y+k x>=y+k, y y y 会变得尽量大,和最长路一样, y y y 可以用 k k k 更新 x x x,因此建边
add(y, x, k)
当然转化为最短路和最长路并不是固定的,二者可以相互转化
x
−
y
>
=
k
x-y>=k
x−y>=k 可以写成
y
<
=
x
−
k
y<=x-k
y<=x−k,就变成了最短路形式,建边代码如下
add(x, y, -k)
一般设超级源点主要是为了判断是否存在解,即是否存在负环
- 题目要求输出一组解的时候,从虚拟节点开始跑即可
- 有时候需要输出 n n n 比 1 1 1 大多少,或者区间 [ 1 , n ] [1,n] [1,n] 的答案,求这个答案一般是从 1 1 1 开始跑
- 判断是否存在解时也要从虚拟节点开始跑
P5960 【模板】差分约束算法
思路:
题解
建立超级源点,将超级源点向所有边连一条长度为
0
0
0 的边,
s
p
f
a
spfa
spfa 跑最短路
如果有负环则无解
否则超级源点到所有点的最短路即为一组解,
d
i
s
[
i
]
dis[i]
dis[i] 即为
x
i
x_i
xi
得到
x
i
<
=
0
x_i<=0
xi<=0 所有
x
x
x 的最大解
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn], vis[maxn], num[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
bool spfa(int s){
queue <int> q;
mem(dis, 0x3f);
dis[s] = 0;q.push(s);
while(!q.empty()){
int x = q.front();q.pop();vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;
q.push(to);
++num[to];
if(num[to] > n + 1) return 0;
}
}
}
}
return 1;
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) add(0, i, 0);//建超级源点
for(int i = 1; i <= m; ++i){
int x, y, z;cin >> x >> y >> z;
add(y, x, z);
}
if(!spfa(0)) cout << "NO";
else {
for(int i = 1; i <= n; ++i)
cout << dis[i] << " ";
}
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
类似的,建好图
s
p
f
a
spfa
spfa 跑最长路,如果存在正环则无解
得到
x
i
>
=
0
x_i>=0
xi>=0 所有
x
x
x 的最小解
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn], vis[maxn], num[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
bool spfa(int s){
queue <int> q;
mem(dis, -1);
dis[s] = 0;num[s] = 1;q.push(s);
while(!q.empty()){
int x = q.front();q.pop();vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] < dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;
q.push(to);
++num[to];
if(num[to] > n + 1) return 0;
}
}
}
}
return 1;
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) add(0, i, 0);//建超级源点
for(int i = 1; i <= m; ++i){
int x, y, z;cin >> x >> y >> z;
add(x, y, -z);
}
if(!spfa(0)) cout << "NO";
else {
for(int i = 1; i <= n; ++i)
cout << dis[i] << " ";
}
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
4247. 糖果
思路:
不用建立超级源点
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn];
bool vis[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
void spfa(int s){
queue <int> q;
mem(dis, 0x3f);
dis[s] = 0;q.push(s);
while(!q.empty()){
int x = q.front();q.pop();vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;
q.push(to);
}
}
}
}
}
void work()
{
cin >> n >> m;
//for(int i = 1; i <= n; ++i) add(0, i, 0);
for(int i = 1, x, y, z; i <= m; ++i){
cin >> x >> y >> z;add(x, y, z);
}
spfa(1);
cout << dis[n] << " ";
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
P6145 [USACO20FEB]Timeline G
思路:
建立超级源点到每个节点的边,边权即为节点最早出现的时间
然后建图跑拓扑排序求最长路即可,最后输出一组解
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn];
ll c;
int in[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
void topsort(){
queue <int> q;
q.push(0);
while(!q.empty()){
int x = q.front();q.pop();
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
in[to]--;
if(!in[to]) q.push(to);
dis[to] = max(dis[to], dis[x] + e[i].w);
}
}
}
void work()
{
cin >> n >> m >> c;
for(int i = 1; i <= n; ++i) {
int x;cin >> x;add(0, i, x);//建超级源点
in[i]++;
}
for(int i = 1; i <= c; ++i){
int x, y, z;cin >> x >> y >> z;
add(x, y, z);in[y]++;
}
topsort();
for(int i = 1; i <= n; ++i) cout << dis[i] << endl;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
P1250 种树
思路:
这个题问的是输出满足所有不等式的前提下,种树最少,其实就是最小解,思考方向就是跑最长路了
这个题的条件是约束区间和,显然可以用前缀和的两个端点约束,差分约束的题目一般都会有隐含的约束条件,需要注意。这样建完图后其实就是求区间
1
1
1 到
n
n
n 的前缀和,也就是从
0
0
0 开始跑长路,这个题中
0
0
0 节点是要用来维护前缀和的,不能作为虚拟节点。最后答案即为
d
i
s
[
n
]
dis[n]
dis[n]。
[
b
,
e
]
[b,e]
[b,e] 区间内树的数量至少为
c
c
c,转化为
s
u
m
[
e
]
−
s
u
m
[
b
−
1
]
>
=
c
sum[e]-sum[b-1]>=c
sum[e]−sum[b−1]>=c
每个位置看成节点,那么即为
b
−
1
b-1
b−1 向
e
e
e 连一条长度为
c
c
c 的边
又因为每个位置至多只能种一棵树
因此还需要满足
s
u
m
[
i
]
−
s
u
m
[
i
−
1
]
<
=
1
sum[i]-sum[i-1]<=1
sum[i]−sum[i−1]<=1 以及
s
u
m
[
i
]
−
s
u
m
[
i
−
1
]
>
=
0
sum[i]-sum[i-1]>=0
sum[i]−sum[i−1]>=0
最长路建边即为
add(i, i - 1, -1);add(i - 1, i, 0);
跑最长路,虚拟节点连节点时边权设
i
n
f
inf
inf,看题目的边权范围,这个题设
1
0
5
10^5
105 就行
博主有解释原因,但是不是非常能理解
因此如果需要求最长路,我们可以先转化为最长路的形式,然后对边权取负求最短路,最后对答案再取负即可,这样就不用考虑虚拟节点连边的权值问题
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn], vis[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
void spfa(int s){
queue <int> q;
for(int i = 1; i <= n; ++i) dis[i] = -1e9;
dis[s] = 0;q.push(s);
while(!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] < dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;q.push(to);
}
}
}
}
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) add(i - 1, i , 0), add(i, i - 1, -1);
for(int i = 1; i <= m; ++i){
int x, y, z;cin >> x >> y >> z;
add(x - 1, y, z);
}
for(int i = 0; i <= n; ++i) add(n + 1, i, 1e5);
spfa(n + 1);
cout << dis[n] - 1e5;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
这个代码大体思路和上边的一样,只不过把边权取负,然后跑最短路(虚拟节点连其他点边权为
0
0
0),最后对答案取负就好了
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn], vis[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
void spfa(int s){
queue <int> q;
mem(dis, 0x3f);
dis[s] = 0;q.push(s);
while(!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;q.push(to);
}
}
}
}
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) add(i - 1, i , 0), add(i, i - 1, 1);
for(int i = 1; i <= m; ++i){
int x, y, z;cin >> x >> y >> z;
add(x - 1, y, -z);
}
for(int i = 0; i <= n; ++i) add(n + 1, i, 0);
spfa(n + 1);
cout << -dis[n];
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
P1260 工程规划
思路:
判断是否存在负环,存在则无解
如果有解,根据题目约束跑最短路即可,最后维护出
M
i
n
=
m
i
n
(
d
i
s
[
1
]
,
d
i
s
[
2
]
,
.
.
.
d
i
s
[
n
]
)
Min=min(dis[1],dis[2],...dis[n])
Min=min(dis[1],dis[2],...dis[n])
最后的
d
i
s
[
i
[
=
d
i
s
[
i
]
−
M
i
n
dis[i[=dis[i]-Min
dis[i[=dis[i]−Min,输出一组解
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn], vis[maxn], num[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
void spfa(int s){
queue <int> q;
mem(dis, 0x3f);
dis[s] = 0;q.push(s);num[s]++;
while(!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;q.push(to);++num[to];
if(num[to] > n){
cout << "NO SOLUTION";return;
}
}
}
}
}
int Min = inf;
for(int i = 1; i <= n; ++i) Min = min(Min, dis[i]);
for(int i = 1; i <= n; ++i) cout << dis[i] - Min << endl;
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) add(0, i, 0);
for(int i = 1; i <= m; ++i){
int x, y, z;cin >> x >> y >> z;
add(y, x, z);
}
spfa(0);
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
P4878 [USACO05DEC]Layout G
思路:
转化一下题目问题:
存在负权环则无解
1
1
1 和
n
n
n 不连通,则输出
−
2
-2
−2
存在解,因为是要求最远距离,输出
1
1
1 到
n
n
n 的最短路即可
先从
0
0
0 跑一遍判负环
然后从
1
1
1 跑一遍求答案
ps:
想了半天为什么相邻节点要建边权为
0
0
0 的边
突然发现题目要求牛牛之间的排队要按照编号来,并且不同编号的牛可以在同一个位置
设两个牛牛的编号为
i
,
j
i,j
i,j,那么必须满足
i
<
j
i<j
i<j 时,
x
i
<
=
x
j
x_i<=x_j
xi<=xj,即
x
i
<
=
x
j
+
0
x_i<=x_j+0
xi<=xj+0
这是隐含的约束条件
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt, num[maxn];
bool vis[maxn];
ll dis[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
int spfa(int s){
queue <int> q;mem(dis, 0x3f);dis[s] = 0;
q.push(s);
while(!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
++num[to];vis[to] = 1;q.push(to);
if(num[to] > n) return -1;
}
}
}
}
return (dis[n] == INF ? -2 : dis[n]);
}
void work()
{
int ml, md;
cin >> n >> ml >> md;
for(int i = n; i >= 2; --i) add(i, i - 1, 0);
for(int i = 1; i <= ml; ++i){// 题目保证了 x < y,根据题意如果不保证就需要交换一下
int x, y, z;cin >> x >> y >> z;if(x > y) swap(x, y);
add(x, y, z);// y - x <= z
}
for(int i = 1; i <= md; ++i){
int x, y, z;cin >> x >> y >> z;if(x > y) swap(x, y);
add(y, x, -z);// y - x >= z ---> x - y <= -z
}
for(int i = 1; i <= n; ++i) add(0, i, 0);
if(spfa(0) == -1) cout << -1 << endl;
else{
cout << spfa(1);
}
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
P1993 小 K 的农场
思路:
这个题纯纯模板,啥坑也没有
判断是否存在合法解,根据约束关系建图,建超级源点判负环,
s
f
p
a
sfpa
sfpa 跑最短路就好了
P2294 [HNOI2005]狡猾的商人
思路:
题解
这个题和种树类似,都是前缀和
但是这个题约束区间和的是等式,而非不等式
s
u
m
[
t
]
−
s
u
m
[
s
−
1
]
=
x
sum[t]-sum[s-1]=x
sum[t]−sum[s−1]=x 转化为
s
u
m
[
t
]
−
s
u
m
[
s
−
1
]
>
=
x
sum[t]-sum[s-1]>=x
sum[t]−sum[s−1]>=x 和
s
u
m
[
t
]
−
s
u
m
[
s
−
1
]
<
=
x
sum[t]-sum[s-1]<=x
sum[t]−sum[s−1]<=x
然后建超级源点
n
+
1
n+1
n+1,跑最短路判负环就好了,因为跑最短路超级源点连边的权值可以为
0
0
0,而最长路有时候设
0
0
0 不行
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 4e3 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn], vis[maxn], num[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
string spfa(int s){
queue <int> q;
mem(dis, 0x3f);dis[s] = 0;q.push(s);
while(!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;q.push(to);++num[to];
if(num[to] > n) return "false";
}
}
}
}
return "true";
}
void init(){
mem(head, 0);cnt = 0;mem(vis, 0);mem(num, 0);
}
void work()
{
cin >> n >> m;
init();
for(int i = 1; i <= m; ++i){
int s, t, v;cin >> s >> t >> v;
add(s - 1, t, v);add(t, s - 1, -v);
}
for(int i = 0; i <= n; ++i) add(n + 1, i, 0);
cout << spfa(n + 1) << endl;
}
int main()
{
ios::sync_with_stdio(0);
int TT;cin>>TT;while(TT--)
work();
return 0;
}
3275 [SCOI2011]糖果
思路:
这个题数据不强,
s
p
f
a
spfa
spfa 可以过
连通性博客
这个博客里银河这道题数据强(虽然标的数据强,其实挺弱的,spfa仍然能过
这个题主要是建图,上边博客也有写,这里不就写了
SP116 INTERVAL - Intervals
思路:
因为它要求取的整数互不相同,所以几乎和前边的种树一模一样,就是输入的节点范围不是
[
1
,
n
]
[1,n]
[1,n],稍微一下种树代码就好了
注意:这道题显然应该跑最长路,我的写法只不过把所有权值取负,转化成跑最短路
362. 区间
和这个题一样,但是数据稍微强一些,区间左端点会取到
0
0
0,这区间时候需要往右移动一位
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 2e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt;
int dis[maxn], vis[maxn];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
void init(){
mem(vis, 0);mem(head, 0);cnt = 0;
}
void spfa(int s){
queue <int> q;
mem(dis, 0x3f);
dis[s] = 0;q.push(s);
while(!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;q.push(to);
}
}
}
}
}
void work()
{
cin >> n;
init();
int s = 5e4 + 2;
for(int i = 1; i <= s - 1; ++i) add(i - 1, i , 0), add(i, i - 1, 1);
int Max = 0;
for(int i = 1; i <= n; ++i){
int x, y, z;cin >> x >> y >> z;
++x,++y;// 都向右移一位就好了
add(x - 1, y, -z);
Max = max(Max, y);
}
for(int i = 0; i <= s - 1; ++i) add(s, i, 0);
spfa(s);
cout << -dis[Max] << endl;
}
int main()
{
ios::sync_with_stdio(0);
int TT;cin>>TT;while(TT--)
work();
return 0;
}
393. 雇佣收银
思路:
博主讲的挺好
设
d
[
i
]
d[i]
d[i] : 代表
i
i
i 时刻的收银员人数
r
[
i
]
r[i]
r[i] 代表
i
i
i 时候至少需要的收银员人数
d
i
s
[
i
]
dis[i]
dis[i]:代表
0
∼
i
0\sim i
0∼i 这个时间段收银员的人数。(前缀和数组)
暂时不是非常能理解
每个位置能选的人数应该大于等于
0
0
0 并且小于等于最多的人数,即
d
i
s
[
i
]
−
d
i
s
[
i
−
1
]
>
=
0
dis[i]-dis[i-1]>=0
dis[i]−dis[i−1]>=0 并且
d
i
s
[
i
]
−
d
i
s
[
i
−
1
]
<
=
d
[
i
]
dis[i]-dis[i-1]<=d[i]
dis[i]−dis[i−1]<=d[i]
i
>
=
8
i>=8
i>=8 时,
i
i
i 时刻的收银人数应该大于所需要的人数,即
d
i
s
[
i
]
−
d
i
s
[
i
−
8
]
>
=
r
[
i
]
dis[i]-dis[i-8]>=r[i]
dis[i]−dis[i−8]>=r[i]
1
<
=
i
<
=
7
1<=i<=7
1<=i<=7 时,
d
i
s
[
i
]
+
d
i
s
[
24
]
−
d
i
s
[
16
+
i
]
>
=
r
[
i
]
dis[i]+dis[24]-dis[16+i]>=r[i]
dis[i]+dis[24]−dis[16+i]>=r[i]
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define mem(x, d) memset(x, d, sizeof(x))
#define eps 1e-6
using namespace std;
const int maxn = 100 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
struct node{
int next, to, w;
}e[maxn];
int head[maxn], cnt, vis[maxn];
int dis[maxn], num[maxn], d[maxn];
int r[30];
void add(int x, int y, int z){
e[++cnt].to = y;
e[cnt].w = z;
e[cnt].next = head[x];
head[x] = cnt;
}
bool spfa(int s24){
mem(head, 0);cnt = 0;
d[0] = 0;
for(int i = 1; i <= 24; ++i){
add(i - 1, i, 0);add(i, i - 1, d[i]);
if(i >= 8) add(i - 8, i, -r[i]);
else add(i + 16, i, s24 - r[i]);
}
add(0, 24, -s24);add(24, 0, s24);
queue<int> q;
mem(dis, 0x3f);mem(vis, 0);mem(num, 0);
dis[0] = 0;q.push(0);
while(!q.empty()){
int x = q.front();q.pop();vis[x] = 0;
for(int i = head[x]; i; i = e[i].next){
int to = e[i].to;
if(dis[to] > dis[x] + e[i].w){
dis[to] = dis[x] + e[i].w;
if(!vis[to]){
vis[to] = 1;++num[to];q.push(to);
if(num[to] >= 25) return 0;
}
}
}
}
return 1;
}
void work()
{
for(int i = 1; i <= 24; ++i) cin >> r[i];
cin >> n;
for(int i = 1, x; i <= n; ++i){
cin >> x;d[++x]++;
}
bool f = 0;
int l = 0, r = n;
while(l < r){
int mid = l + r >> 1;
if(spfa(mid)) r = mid;
else l = mid + 1;
}
if(spfa(l)) cout << l << endl;
else cout << "No Solution" << endl;
}
int main()
{
ios::sync_with_stdio(0);
int TT;cin>>TT;while(TT--)
work();
return 0;
}