种树 题解

原题链接
大致题意:给你 n n n个数,你最多可以选择其中不相邻的 k k k个数,使得其总和最大。

a s d [ i ] [ j ] asd[i][j] asd[i][j]表示到了第 i i i个位置,种了 j j j棵树的最大收益。

转移为:

a s d [ i ] [ j ] = m a x ( a s d [ i − 1 ] [ j ] , a s d [ i − 2 ] [ j − 1 ] + a [ i ] ) asd[i][j]=max(asd[i-1][j],asd[i-2][j-1]+a[i]) asd[i][j]=max(asd[i1][j],asd[i2][j1]+a[i])

需要注意下边界

时间复杂度: O ( n k ) O(nk) O(nk),预计得分 50 p t s 50pts 50pts

#include
using namespace std;
#define f1(a,b,c) for(int c=a;c<=b;c++)
#define f2(a,b,c) for(int c=a;c>=b;c--)
#define f3(a,b,c) for(int c=a;c;c=b)
#define so1(a,n) sort(a+1,a+n+1,mycmp);
#define so2(a,n) sort(a+1,a+n+1);
#define ll long long
#define itn int
#define ubt int 
const int twx=6000+10;
const int inf=0x7fffffff;
ll read()
{
    ll sum=0;
    ll flag=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
        {
            flag=-1;
        }
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        sum=((sum*10)+c-'0');
        c=getchar();
    }
    return sum*flag;
}
int n,k;
int a[twx];
int cyrcyr;
int asd[twx][twx/2];
void init()
{
    n=read();
    k=read();
    f1(1,n,i)
    {
        a[i]=read();
    }
    memset(asd,0,sizeof asd);
}
void work()
{
	asd[1][1]=a[1];
	f1(2,n,i)
    {
        f1(1,k,j)
        {
            asd[i][j]=max(asd[i-1][j],asd[i-2][j-1]+a[i]);
        }
    }
}
void print()
{
	f1(1,k,i)
	{
		cyrcyr=max(asd[n][i],cyrcyr); 
	}
	printf("%d\n",cyrcyr);
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    init();
    work();
    print();
	return 0;
}

但是由于本题数据量达到了 5 e 5 5e5 5e5 d p dp dp不能拿全分,考虑优化。

首先考虑如何减小枚举规模,

k = 1 k=1 k=1时,显然可以直接取 n n n个数中最大的数。

设最大的数为 a [ i ] a[i] a[i]

k = 2 k=2 k=2时,则可以知道有两种情况:

1.另外取一个与 a [ i ] a[i] a[i]不相邻的 a [ j ] a[j] a[j]

2.取 a [ i − 1 ] a[i-1] a[i1] a [ i + 1 ] a[i+1] a[i+1]而不选 a [ i ] a[i] a[i]

可以发现:如果 k = 1 k=1 k=1时的最优解是 a [ i ] a[i] a[i],那么就能吧 a [ i − 1 ] a[i-1] a[i1] a [ i + 1 ] a[i+1] a[i+1]合并,因为它们要么同时被选,要么全部都不选。

证明:当 k = 2 k=2 k=2时,要么 a [ i − 1 ] a[i-1] a[i1] a [ i + 1 ] a[i+1] a[i+1]都要被选要么都不选才能满足贪心,因为如果只选其一的话,它们中的任意一个都比 a [ i ] a[i] a[i]小。

当我们选择了 a [ i − 1 ] a[i-1] a[i1] a [ i + 1 ] a[i+1] a[i+1]时,获利就是 a [ i − 1 ] + a [ i + 1 ] − a [ i ] a[i-1]+a[i+1]-a[i] a[i1]+a[i+1]a[i]

所以当 a [ i ] a[i] a[i]被选时,就把 a [ i − 1 ] a[i-1] a[i1] a [ i + 1 ] a[i+1] a[i+1]删去,并把 a [ i ] a[i] a[i]改成 a [ i − 1 ] + a [ i + 1 ] − a [ i ] a[i-1]+a[i+1]-a[i] a[i1]+a[i+1]a[i],重新去寻找最大值。

由于每次都是寻找最大值,所以可以用堆进行操作,直到堆中最大值小于 0 0 0或取出 k k k个数之后停止。

时间复杂度 O ( k log ⁡ n ) O(k\log n) O(klogn),预计得分 100 p t s 100pts 100pts

#include
using namespace std;
#define f1(a,b,c) for(int c=a;c<=b;c++)
#define f2(a,b,c) for(int c=a;c>=b;c--)
#define f3(a,b,c) for(int c=a;c;c=b)
#define so1(a,n) sort(a+1,a+n+1,mycmp);
#define so2(a,n) sort(a+1,a+n+1);
#define ll long long
#define itn int
#define ubt int 
const int twx=2e6+100;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll read()
{
    ll sum=0;
    ll flag=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
        {
            flag=-1;
        }
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        sum=((sum*10)+c-'0');
        c=getchar();
    }
    return sum*flag;
}
struct LV
{
    ll x;
    ll p;
    bool operator < (const LV & a) const 
    {
        return x<a.x;
    }
};
priority_queue<LV> q;
ll k,m,n;
LV y;
ll l[twx];
ll r[twx];
ll b[twx];
ll a[twx];
void init()
{
	n=read();
    k=read();
    m=0;
    f1(1,n,i)
    {
        a[i]=read();
        l[i]=i-1;
        r[i]=i+1;
        LV s;
        s.x=a[i];
        s.p=i;
        q.push(s);
    }
}
void work()
{
    a[0]=-inf;
    a[n+1]=-inf;
    l[0]=0;
    r[0]=1;
    l[n+1]=n;
    r[n+1]=n+1;
    f1(1,k,i)
    {
        while((b[q.top().p]==-1)&&(!q.empty()))
        {
            q.pop();
        }
        y=q.top();
		if(q.empty()) 
        {
            break;
        }
        q.pop();
        if(y.x<=0) 
        {
            break;
        }
        m=m+y.x;
        y.x=a[l[y.p]]+a[r[y.p]]-a[y.p];
        a[y.p]=y.x;
        b[l[y.p]]=-1;
        b[r[y.p]]=-1;
        r[l[l[y.p]]]=y.p;
        l[r[r[y.p]]]=y.p;
        l[y.p]=l[l[y.p]];
        r[y.p]=r[r[y.p]];
        q.push(y);
    }
}
void print()
{
	printf("%lld\n",m);
}
int main()
{
    init();
    work();
    print();
	return 0;
}

你可能感兴趣的:(日常刷题记录)