luogu1250 种树

论:如何生硬的将最少值改成小于的方法走spfa找最短路

先说差分约束在这题的用法

居民想在[a,b]区间内中最少c种棵树

首先要明确:每个区域内最多只能种一棵树

所以用一个数组sum[i]:表示1到i区域至少有多少数

所以sum[i]~sum[i-1]中有0~1棵数,列成不等式就是

sum[i]-sum[i-1]<=1

sum[i-1]-sum[i]<=0

在给定的a到b之间最少有c棵数列成不等式就是

a-b>=c(看到这个是不是很懵,但是在这里它要转换为:b-a<=-c 的形式)

b-a<=-c(因为这题要求最小值)

所以 sum[b]-sum[a-1]<=-c(包括a点本身)

按照这样的关系建链,再用spfa找出最短路径

但是,这题不一定保证所有的点都互相联通,所以要建立一个虚拟根联通所有点,来引导其他点的计算

使所有的点到虚拟根的权值(也就是种树的最少值)为0

sum[n+1]~sum[i]<=0

最后对所求答案ans[n]-min(ans[0~n]),因为差分约束是点与点之间的关系,本题要求答案尽可能的小,所以在不影响原本的点与点的关系的情况下,要缩小最优解

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100001;
struct llk
{
  int up;
  int zd;
  int vis;
}w[maxn*2];
int h[maxn],top,ans[maxn],bj[maxn];
void pop(int next,int end,int viss)
{
  w[++top].up=h[next];
  w[top].zd=end;
  w[top].vis=viss;
  h[next]=top;
}
queue<int>mp;
int n;
void spfa(int s)
{
  for(int i=0;i<=n+1;i++)
  ans[i]=1e9;
  bj[s]=1;
  ans[s]=0;
  mp.push(s);
  while(!mp.empty())
  {
    //cout<<"1"<<endl;
    int x=mp.front();
    bj[x]=0;
    mp.pop();
    for(int i=h[x];i!=0;i=w[i].up)
    {
      int v=w[i].zd;
      if(ans[x]+w[i].vis<ans[v])
      {
        ans[v]=ans[x]+w[i].vis;
        if(bj[v]==0)
        {
          bj[v]=1;
          mp.push(v);
        }
      }
     }
    }
}
int main()
{
  int m,a,b,c,s;
  cin>>n>>m;
  s=n+1;//虚拟节点
  for(int i=0;i<=n;i++)
  {
    pop(s,i,0);
  }
  for(int i=1;i<=n;i++)
  {
    pop(i,i-1,0);
    pop(i-1,i,1);
  }
  for(int i=1;i<=m;i++)
  {
    cin>>a>>b>>c;
    pop(b,a-1,-c);
  }
  spfa(s);
  int minn=1e9;
  for(int i=0;i<=n;i++)
  {
    //cout<<ans[i]<<" ";
    minn=min(minn,ans[i]);
  }
  //cout<<endl;
  cout<<ans[n]-minn;
  return 0;
}

原文地址:https://www.cnblogs.com/qyh2003/p/9100454.html