种了一下午的树,终于给搞明白了((多谢各位大神的题解)(题解就不能讲清楚点吗(看不见看不见)));
你有k个树,你可以种在一条直线上,每个位置都有一个价值,如果你把树种在这里就可以获得这个价值,但是条件是不能有任意两棵树相邻;
当然你可以种0到k任意棵树;
怎么办呢?这道题也是一个(少见)的良心题;我先写了个无脑20分暴力,枚举这个位置选还是不选;
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=5e5+10; typedef long long ll; int n,k; int a[maxn]; int b[maxn]; ll ans; ll cnt; void dfs(int x) { if(x==n+1) { ll sum=0; int num=0; for(int i=1;i<=n;i++) { if(b[i]==1&&b[i-1]==1) return ; if(b[i]==1) { num++; if(num>k) return ; sum+=(ll)a[i]; } } ans=max(ans,sum); return ; } b[x]=0;dfs(x+1); b[x]=1;dfs(x+1); } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]); dfs(1); printf("%lld",ans); return 0; }
但是怎么能甘心20分,想到这个是一个直线,可能用dp,随便糊了一个就过了50分;
就是f[i][j]表示当前从1到i位置一共种了j棵树最大价值;
如果当前不种树,直接复制f[i-1][j],要是想种树,就复制f[i-2][j-1](因为不能相邻)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=5e5+10; typedef long long ll; int n,k; ll a[maxn]; ll ans; ll f[6010][3010]; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); f[1][1]=a[1]; for(int i=2;i<=n;i++) { for(int j=1;j<=k;j++) { f[i][j]=max(f[i-1][j],f[i-2][j-1]+a[i]); ans=max(ans,f[i][j]); } } printf("%lld",ans); return 0; }
空间时间限制,我不能拿满分,好气哦;
终于,我打开了题解(眼睛放出光芒)
three months later……
我们不用管什么谁先种谁后种,只要最大值;
我们可以用优先队列,这个是怎么想出来的呢?面对连续的3个数(首位或末尾位置随意),标号1,2,3;
如果我们选2号坑,那么1,3就废了;
如果选1.3,那么2就不能选了;(我表示不想分析这种事情)(优先队列帮您实现!!)
我们默认堆顶就是我们要找的最大值,但是我们需要经过一些处理;
将每个点的左右端位置记录下来l[i]=i-1;r[i]=i+1;
先将n个坑扔进去,那么堆顶将是价值最大的那个,我们取出来ans+=val(为什么,1,3怎么办?)
没关系,因为我们选1就不能选1,3,那我们就不选1,3了,将2的值取出后再更新2的值,
val[2]=val[1]+val[3]-val[2];(ans需要更新的值)
但是我们要避免重复选取,将vis[1]=vis[3]=1;(不在特殊访问)
还有就是这样做相当于分成了三个一组三个一组,我们将2的值更新后要将左右位置更新(相当于变成了0,2,4)
这样我们其实解决了三个一组首尾会相连的问题(如果我们要选1,3,我们会再次更新2.此时是0,2,4,模式,他们就不会相连;)
(在选1,3时已经和0,4比较了)
处理完成;
当堆里都是负数的时候就直接退出就行了;
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int maxn=5e5+10; typedef long long ll; int a[maxn]; int n,k; int l[maxn],r[maxn]; priority_queue<pair<int,int> > q; ll ans; int vis[maxn]; int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); //a[i].val=-a[i].val; q.push(make_pair(a[i],i)); l[i]=i-1; r[i]=i+1; } r[0]=1;l[n+1]=n; while(k--) { while(vis[q.top().second]) q.pop(); int x=q.top().first; int i=q.top().second; q.pop(); if(x<0) break; ans+=(ll)x; a[i]=a[l[i]]+a[r[i]]-a[i]; vis[l[i]]=1;vis[r[i]]=1; l[i]=l[l[i]];r[l[i]]=i; r[i]=r[r[i]];l[r[i]]=i; q.push(make_pair(a[i],i)); } printf("%lld",ans); return 0; }
看这些做法和代码长度,你觉得这题(它难吗)(逃~);