Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目描述
详见 【洛谷】种树——区间与贪心的不解之缘 - 掘金 (juejin.cn)
昨天我们使用贪心问题求解了这道题,不过其实这道题有个更有意思的解法。
题目思路--除了贪心...
观察题意,我们其实可以把题目转化为一系列的不等式组,然后求在满足不等式组的条件下某一变量的最值。
首先,设 为区域 至 之间种的树的数量。那么根据题目,我们可以列出以下的方程组:(注意是包括b和e的)
别忘了还有条件“每个部分为一个单位尺寸大小并最多可种一棵树。”,另外,有个暗含条件是每个单位尺寸的树的数量不为负数。
最后我们要求的是 f_n 的最小值。怎么求呢?
又见差分约束系统!
【学海拾遗】不等式组求解——图论的巧妙使用 - 掘金 (juejin.cn)
根据这篇文章,我们得知了不等式组的求解可以转化为图论的最短路问题。
但是这里有点小问题:
- 首先这个题要求的是一个特定的解,其中要求最小。
对于这种求最大或者最小的特定解的问题,我们也有通用解法,那就是 寻找一个点,它的肯定为0,就以这个点为起点,求目标结点的最短路即可~
在这里我们找到的点就是对于的点,因为。
- 其次,这里的等号都是小于号,如果转化为大于号,就会出现一堆边权为 的边。
出现负权边倒是没问题,但是我们惊讶的发现这样建图,是不能从到达点的,会得出无解!
因此我们换个思路:求最长路即可。
固定一个结点 , 设 为图中结点 到结点 的最长路径。 那么当 与 之间有一条有向边,边权为 时,有以下性质:
然后上面的不等式组就可以转换成如下的图:
图有个结点,由于 必为 ,那我们固定 , 即图中的最长路径是指从出发的最长路径。
对于 ,从向 建一条权值为 的边;
对于 ,从 向 建一条权值为 的边,从 向 建一条权值为 的边;
最后使用SPFA求最长路即可。
为什么是最长路?
可能有人会疑惑,为什么题目 求的是 的最小值,但我们却求得是图的最长路径??? 为什么不求最短路径呢?
大家可以仔细想想。当我们根据那些不等式建图的时候,其实我们已经定下了 的含义,那就是最长路径, 而不是最短路径!如果我们用最短路径去求,很显然得出来的一系列 是不满足上述的不等式组的。
而我们求出了最长路径时,就意味着我们得到了一个等于号, 也就是说存在一个结点 ,有 ,这意味着什么?意味着我们求出来了不等式组的一个临界解。而临界解通常意味着最值,并且无论是最大值还是最小值,每个情景绝对只有一个最值。 在这里是最小值,因此我们就可以把它当作结果了。总而言之,建模时求图的“最长“路径还是”最短”路径和解题时求的“最大“值“最小“值没有关系,我们只需要确定我们得到了”最“,我们就可以把它作为答案。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cctype>
#include<string>
#include<cmath>
#include<cstring>
#include<queue>
#include<numeric>
#define fru(a,b,c) for(int a=b;a<=c;a++)
#define frd(a,b,c) for(int a=b;a>=c;a--)
#define fr(a,b) for(int a=0;a<b;a++)
#define pb push_back
#define mp make_pair
#define sof sizeof
using namespace std;
using ll=long long;
using db=double;
ll rd() {
ll k = 0, f = 1;
char c = getchar();
while (c < '0' || c>'9') {
if (c == '-')f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
k = (k << 1) + (k << 3) + (c ^ 48);
c = getchar();
}
return f > 0 ? k : -k;
}
const db eps=1e-6;
const int inf=0x3ffffff3;
const ll linf=0x3ffffffffffffff3;
const db dinf=1e10;
const double pi=acos(-1);
inline bool is0(db a){return a < eps && a > -eps;}
const int maxn=100000+5;
struct edge{
int v,w;
};
vector<edge> graph[maxn];
bool fd[maxn];
int dist[maxn];
int timee[maxn];
bool SPFA(int n){
queue<int> q;
fill_n(dist,n+1,-inf);
dist[0]=0;
fd[0]=1;
q.push(0);
while(!q.empty()){
int u=q.front();q.pop();
fd[u]=0;
for(auto e:graph[u]){
int v=e.v,w=e.w;
if(dist[u]+e.w>dist[v]){
dist[v]=dist[u]+e.w;
if(!fd[v]){
q.push(v);
fd[v]=1;
timee[v]++;
if(timee[v]>n)return 0;
}
}
}
}
return 1;
}
int main(){
int n=rd(),h=rd();
fr(i,h){
int b=rd(),e=rd(),t=rd();
graph[b-1].pb({e,t});
}
fru(i,1,n){
graph[i-1].pb({i,0});
graph[i].pb({i-1,-1});
}
SPFA(n);
cout<<dist[n];
}