dp题

1.luogu 1484种树

50分思路:dp,但是数据规模过大没法dp选择奇怪贪心

dp方程 到i坑种j树 dp[i][j]=max(dp[i-1][j],dp[i-2][j-1])

100分思路:奇怪贪心,全部树坑中最大的i ,每次选择实际上需要选的是max(a[i],a[l]+a[r]);

但是如此转移十分困难,可以进行如下操作:

1.ans+=a[i]

2.设立新节点,利用链表存储各节点间位置关系,新节点的值为a[l]+a[r]-a[i],若日后选入新节点,意味着实际上选择了i两侧的点,未选则最终选择就是i

3.利用优先队列维护上述值,需要取出堆顶值为负则停止进行,并且需要取小于限制的数量

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N=500010;
struct node{
    ll v;int id;
    bool operator <(node a)const{
    return v<a.v;}};
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;}
ll a[N],ans;
int l[N],r[N],vis[N];
priority_queue<node> q;
int n,k;
int main(){
    n=read();k=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        q.push((node){a[i],i});
        l[i]=i-1;r[i]=i+1;
    }int len=n;
    for(int i=1;i<=k;i++){
        while(!q.empty()&&vis[q.top().id]) q.pop();
        if(q.empty()||q.top().v<0)
            break;
        node u=q.top();q.pop();
        ans+=u.v;
        vis[u.id]=vis[l[u.id]]=vis[r[u.id]]=1;
        a[++len]=a[l[u.id]]+a[r[u.id]]-a[u.id];
        l[len]=l[l[u.id]],r[len]=r[r[u.id]];
        r[l[len]]=len;l[r[len]]=len;
        q.push((node){a[len],len});
    }printf("%lld\n",ans);
    return 0;
}

 2.最大子段和

转移方程:f[i]=max(a[i],f[i-1]+a[i]) 最大子段和

e.g:给为n序列a,f(l,r)=i由l到r,求g[i]*(-1)的(r-i)次方 ,1<=l<=r<=n,求f(l,r)max

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
ll b[100005],c[100005],g[100050],ans;
int main(){int n;scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&g[i]);
        if(i&1) g[i]=-g[i];}
    for(int i=1;i<=n;i++){
        b[i]=max(g[i],b[i-1]+g[i]);
        c[i]=min(g[i],c[i-1]+g[i]);}
    for(int i=1;i<=n;i++){
        if(i&1) ans=max(ans,-c[i]);
        else ans=max(ans,b[i]);}
    printf("%lld\n",ans);return 0;
}

 3.dp数方案数的模板题

放置某个东西,限制:相隔k个求总方案数 利用前缀和前缀dp

#include<bits/stdc++.h>
#define LL long long
#define rep(i,x,y) for(register int i=x;i<=y;i++)
using namespace std;
const int N=100500;
const int mod=5000011; 
LL n,k,dp[N],s[N];
int main(){
    scanf("%lld%lld",&n,&k);
    rep(i,1,n) dp[i]=1;
    rep(i,1,n){
        if(i>k) dp[i]=(dp[i]+s[i-1-k])%mod;
        s[i]=(s[i-1]+dp[i])%mod;}
    printf("%d",s[n]+1);return 0;}
posted @ 2018-09-02 09:04  ASDIC减除  阅读(182)  评论(0编辑  收藏  举报