P1250 种树
P1250 种树
题目描述
一条街的一边有几座房子。因为环保原因居民想要在路边种些树。路边的地区被分割成块,并被编号成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。
输出格式:
输出文件只有一行写有树的数目
输入输出样例
选尽量少的的数,并且符合对每一个区间的要求。
典型的差分约束,因为是求最小值,所以要用最长路来求,我们设f[i]表示1~i个数中至少选的数的个数,对于每一个区间要求,我们就从B-1向E连一条长度为T的边,表示1~E中选的数的个数至少比1~B-1中选的数的个数多T,也就是B~E中至少选T个数。然而这样是不行的,因为题目中还有隐藏条件,首先十分显然的一个条件f[i+1]>=f[i],因为前i+1个数不可能比前i个数少,所以从i向i+1连一条权值为0的边,其次,对于每个数我们最多只能选一次,所以f[i+1]<=f[i]+1,但是这样不太好,我们移一下项f[i]>=f[i+1]-1,这样我们就要从i+1向i连一条边长为-1的边,最后从一号点跑一遍最长路就好了。
1 #include<iostream> 2 #include<string> 3 #include<cmath> 4 #include<cstring> 5 #include<cstdio> 6 #include<stack> 7 #include<queue> 8 #define maxn 100005 9 using namespace std; 10 11 inline int read() 12 { 13 char c=getchar(); 14 int x=1,res=0; 15 while(c<'0'||c>'9') 16 { 17 if(c=='-') 18 x=-1; 19 c=getchar(); 20 } 21 while(c>='0'&&c<='9') 22 { 23 res=res*10+(c-'0'); 24 c=getchar(); 25 } 26 return x*res; 27 } 28 29 struct edge 30 { 31 int next,to,dis; 32 }g[maxn<<1]; 33 int n,m,aa,bb,cc,num; 34 int last[maxn],c[maxn],vis[maxn]; 35 queue<int>q; 36 37 void add(int from,int to,int dis) 38 { 39 g[++num].next=last[from]; 40 g[num].to=to; 41 g[num].dis=dis; 42 last[from]=num; 43 } 44 45 int main() 46 { 47 n=read();m=read(); 48 for(int i=1;i<=m;i++) 49 { 50 aa=read();bb=read();cc=read(); 51 add(aa-1,bb,cc); 52 } 53 for(int i=0;i<=n;i++) 54 { 55 add(i-1,i,0); 56 add(i,i-1,-1); 57 c[i]=-100000000; 58 } 59 c[0]=0; 60 q.push(0); 61 while(q.size()) 62 { 63 int u=q.front(); 64 q.pop(); 65 vis[u]=0; 66 for(int i=last[u];i;i=g[i].next) 67 { 68 int v=g[i].to; 69 if(c[v]<c[u]+g[i].dis) 70 { 71 c[v]=c[u]+g[i].dis; 72 if(!vis[v]) 73 { 74 vis[v]=1; 75 q.push(v); 76 } 77 } 78 } 79 } 80 printf("%d\n",c[n]); 81 return 0; 82 }