原题链接
大致题意:给你 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[i−1][j],asd[i−2][j−1]+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[i−1]和 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[i−1]和 a [ i + 1 ] a[i+1] a[i+1]合并,因为它们要么同时被选,要么全部都不选。
证明:当 k = 2 k=2 k=2时,要么 a [ i − 1 ] a[i-1] a[i−1]和 a [ i + 1 ] a[i+1] a[i+1]都要被选要么都不选才能满足贪心,因为如果只选其一的话,它们中的任意一个都比 a [ i ] a[i] a[i]小。
当我们选择了 a [ i − 1 ] a[i-1] a[i−1]和 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[i−1]+a[i+1]−a[i]。
所以当 a [ i ] a[i] a[i]被选时,就把 a [ i − 1 ] a[i-1] a[i−1]和 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[i−1]+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;
}