1. 程式人生 > >luogu P1250 種樹

luogu P1250 種樹

我來總結一下最常用的兩種辦法

1.貪心

2.差分約束

那麼我們先來講,貪心版《種樹》

大家可能知道有一個題和這個類似,那個是釘釘子而這個是種樹

我們可以借用釘釘子的思路來想,首先這個是讓你求最小值,而且每個人都有自己劃定的區間,並且他們還要求在這段區間內最少種T棵樹。

那麼我們既要滿足最少種樹數,而且要滿足每個人的要求。好在的是,題目中說過區間和區間之間可能會有一段重疊,那麼我們要抓住這個機會盡可能多的在每一段重複區間內多種樹,所以就會產生一個連鎖反應,就是上一個重複區間內種的樹可能會滿足下一個人的要求,那麼這個人就可以略過去,以達到最少數的目的。

(以下是貪心程式碼,體會一下)

#include<bits/stdc++.h>
using namespace std;
const int N = 31000;
int n,m,ans=0;
bool u[N]={0};
struct Edge{
    int x,y,z;
}a[N];
bool cmp(Edge a,Edge b)
{
    return a.y<b.y;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
        cin>>a[i].x>>a[i].y>>a[i].z;
    sort(a
+1,a+m+1,cmp); for(int i=1;i<=m;i++) { int sum=0; for(int j=a[i].x;j<=a[i].y;j++) if(u[j]) sum++;//統計已有的數量 if(sum>=a[i].z) continue;//滿足就繼續 for(int k=a[i].y;k>=a[i].x;k--)//不滿足情況 { if(!u[k]) { u[k]
=1; sum++; ans++;//答案++ if(sum==a[i].z) break;//直到滿足,退出 } } } cout<<ans;//輸出最後答案(即最少的樹的數量) return 0; }

 


 

接下來我們講,差分約束版《種樹》

我們都知道差分約束是用於最短路不等式問題的

這裡我們利用差分約束解決最短路不等式的性質來看

我們想在區間內種最少的樹,所以根據性質

我們可以列出兩個差分約束公式

1.sum[x]-sum[y-1]>=c;(這裡是指在區間[y,x]中至少種c棵樹)

2.0<=sum[x]-sum[x-1]<=1;(這裡是指一個單位長度內最多種1棵樹)

根據公式,我們可以建邊,但是建邊是y+1->y=-1而不是y->y+1=1

建好邊我們就可以跑一邊SPFA啦,最少種樹數也就出來了!

(差分約束程式碼,體會一下)

#include<bits/stdc++.h>
using namespace std;
const int N = 31000;
const int M = 110000;
int n,m;
int dis[N];
bool vis[N];
int head[N],num;
struct Edge{
    int to,next,w;
}s[M];
void add(int u,int v,int w)//根據公式建邊
{
    s[++num].w=w;
    s[num].next=head[u];
    head[u]=num;
    s[num].to=v;
}
void spfa(int x)//SPFA經典操(ban)作(zi)
{
    queue<int> q;
    q.push(x);
    for(int i=0;i<=n+1;i++)
        dis[i]=1;
    dis[x]=0;vis[x]=1;
    while(!q.empty())
    {
        int g=q.front();
        q.pop();
        vis[g]=0;
        for(int i=head[g];i!=-1;i=s[i].next)
        {
            int t=s[i].to;
            if(dis[t]>dis[g]+s[i].w)
            {
                dis[t]=dis[g]+s[i].w;
                if(!vis[t])
                {
                    q.push(t);
                    vis[t]=1;               
                }
            }
        }
    }
}
int main()
{
    int a,b,c,minn=123456789;
    memset(head,-1,sizeof(head));
    cin>>n>>m;
    int y=n+1;
    for(int i=0;i<=n;i++) add(y,i,0);
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        add(b,a-1,-c);
    }
    for(int i=1;i<=n;i++)//建邊操作
    {
        add(i-1,i,1);
        add(i,i-1,0);
    }
    spfa(y);
    for(int i=0;i<=n;i++)//取最小值
        minn=min(minn,dis[i]);
    cout<<dis[n]-minn<<endl;
    return 0;
}

煙雨江南,無你何歡!