传送门
题目描述
一条街的一边有几座房子。因为环保原因居民想要在路边种些树。路边的地区被分割成块,并被编号成1..N。每个部分为一个单位尺寸大小并最多可种一棵树。每个居民想在门前种些树并指定了三个号码B,E,T。这三个数表示该居民想在B和E之间最少种T棵树。当然,B≤E,居民必须记住在指定区不能种多于区域地块数的树,所以T≤E-B+l。居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量。
写一个程序完成以下工作:
输入输出格式
输入格式: 第一行包含数据N,区域的个数(0 < N ≤ 30000);
第二行包含H,房子的数目(0 < H≤5000);
下面的H行描述居民们的需要:B E T,0 < B≤E≤30000,T≤E-B+1。
输出格式: 输出文件只有一行写有树的数目
输入输出样例
输入样例#1:
9
4
1 4 2
4 6 2
8 9 2
3 5 2
输出样例#1:
5
这道题贪心可以做,树状数组 也可以做。然而我们来讲一下差分约束。
简单算法↑ 和 数据结构↑ 比较无聊。然后我们来讲差分约束→_→
这个思想非常有趣~~~下面是精彩的思想部分:
比较重要的地方我在题目描述中用加粗字体标注出来了。
首先,我们知道最短路算法跑完后,对于每一条边满足:
dis[to] <= dis[from] + cost ->①
不知道最短路是啥?
然后,设sum[i]为某个点到起点之间种了几棵树。(起点在哪自己定义,这个题中起点是0)
假设我们已经种完树了,那么必定有sum[E] - sum[B-1] >= T (E、B、T的定义去题面里找)
移项后易得:sum[B-1] <= sum[E] -T -> ②
将①式和②式比对一下,发现了什么→_→
竟然能够对应起来!好玩吧?
既然这个题我们要把区间维护成sum[B-1] <= sum[E] -T 这个样子,那么可不可以用最短路做呢?
关键点来了!
1、有一种边我们是可以确定要建的,那就是①式对应的②式中的E和B-1,权值为-T。很显然,观察下①、②式就看出来了。
从 E 向 B-1 建一条边权为 -T的边。**
2、由条件“每个部分为一个单位尺寸大小并最多可种一棵树”可推导出重要条件:
1 >= sum[i] - sum[i-1] >=0
将这个式子移项变形得到两个式子:
sum[i] <= sum[i-1] +1 ->③
sum[i-1] <= sum[i] +0 ->④
由③可得:从i-1向i建一条边权为1的边。**
由④可得:从i向i-1建一条边权为0的边。**
3、显然 sum[i] <= sum[n+1] +0 -> ⑤
所以从sum[n+1] 向 sum[i] 建一条边权为0的边。**
条件T≤E-B+l 保证了图中没有负环。
建边完成,从n+1开始跑最短路就可以了。
跑完之后-sum[0]就是答案。
Dijkstra在处理负权时的表现非常差(即使这道题没有负环),所以我们用SPFA来跑最短路。
首先,观察⑤式,在每一条边中,from是n+1 ,to是i ;我们要保证式子成立就要从n+1开始跑。
其次,我们建的边肯定要有意义,而通过上面建边的过程可以得出,n+1这个点是没有入度的,除n+1外任何一个点都不可能跑到n+1,所以要从n+1开始跑。
根据上一条好好想想。
怎么改?把所有的边反过来,负权变为正权,正权变为负权,其余不变。
最后sum[0]是答案。
此题关键在于如何建边,在理解了思想之后自己动手试一下,不等式要自己推导出来,切忌背代码!!
代码中dis数组就是sum数组。
#include
using namespace std;
const int maxn=200005;
int n,k,tot,m,ans,head[maxn],nxt[maxn<<1],dis[maxn];
struct Edge{
int f,t,c;
}edge[maxn<<1];
inline int read()
{
char ch;
int nm=0;
bool o=false;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')o=true;
nm=ch-'0';
while((ch=getchar())>='0'&&ch<='9')
nm=nm*10+ch-'0';
if(o)return -nm;
return nm;
}
void build(int f,int t,int c)
{
edge[++tot]=(Edge){f,t,c};
nxt[tot]=head[f];
head[f]=tot;
}
bool used[maxn];
queue<int>q;
void spfa(int s)
{
memset(dis,0x7f,sizeof(dis));
dis[s]=0;
q.push(s);
used[s]=true;
while(!q.empty())
{
int num=q.front();q.pop();
used[num]=false;
for(int i=head[num];i;i=nxt[i])
{
int u=edge[i].f,v=edge[i].t;
if(dis[v] > dis[u] + edge[i].c)
{
dis[v]=dis[u]+edge[i].c;
if(!used[v])
{
q.push(v);
used[v]=true;
}
}
}
}
}
int main()
{
n=read();
m=read();
build(n+1,0,0);
for(int i=1;i<=n;i++)//建边
{
build(i,i-1,0);
build(i-1,i,1);
build(n+1,i,0);
}
for(int i=1;i<=m;i++)
{
int f,t,c;
f=read(),t=read(),c=read();
build(t,f-1,-c);
}
spfa(n+1);
printf("%d",-dis[0]);
return 0;
}