题目:

【背景描述】

一排 N 个数, 第 i 个数是 Ai , 你要找出 K 个不相邻的数, 使得他们的和最大。

请求出这个最大和。

【输入格式】

第一行两个整数 N 和 K。

接下来一行 N 个整数, 第 i 个整数表示 Ai 。

【输出格式】

一行一个整数表示最大和, 请注意答案可能会超过 int 范围

【样例输入】

3 2

4 5 3

【样例输出】

7

【数据范围】

对于 20% 的数据, N, K ≤ 20 。

对于 40% 的数据, N, K ≤ 1000 。

对于 60% 的数据, N, K ≤ 10000 。

对于 100% 的数据, N, K ≤ 100000 , 1 ≤ Ai ≤ 1000000000。

分析:

40分:

写一个n^2的dp,定义dp[ i ][ j ]为选到第i个,选了j个的最大值。转移很简单,要注意转移顺序。

#include<bits/stdc++.h>
using namespace std;
#define ri register int
#define ll long long
#define N 1005
ll read()
{
    ll x=0,fl=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') fl=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
ll dp[N][N][3],n,k,a[N];
int main()
{
    freopen("so.in","r",stdin);
    freopen("so.out","w",stdout);
    n=read(); k=read();
    for(ri i=1;i<=n;++i) a[i]=read(); 
    if(k>n/2+1) { printf("0\n"); return 0; }
    for(ri j=1;j<=k;++j)
     for(ri i=2*j-1;i<=n;++i){
         dp[i][j][0]=max(dp[i-1][j][1],dp[i-1][j][0]);
         dp[i][j][1]=dp[i-1][j-1][0]+a[i];
    }
/*    for(ri i=1;i<=n;++i){
        for(ri j=1;j<=k;++j) printf("%lld ",dp[i][j][0]);
        printf("\n");
    }
     
    printf("0:%lld 1:%lld\n",dp[n][k][0],dp[n][k][1]);*/
    printf("%lld\n",max(dp[n][k][0],dp[n][k][1]));
}
/*
3 2
100000000 300000000 500000000

17 9
4
6
8
1
5
8
2
7
9
7
4
8
5
9
4
1
9
*/
dp 40分

100分:

如果不考虑选了一个就不能选两边的限制,直接排序,取前k个。

那如果我们想先选最大的那个,选了后发现其实选这个数两边的更优,想反悔。

怎么实现这种反悔操作呢?

a b c

令我们现在选了b,实际上选a+c更优,那么就可以把a+c-b放入原序列,下次如果发现其更优,就会把a+c-b选了,相当于反悔选b,而选了a+c。

现在对于这种动态插点排序,用优先队列来维护,并用链表记录左右两边的位置是哪一个。

因为有删数操作,所以要用两个数组l和r,实现双向记录。

#include<bits/stdc++.h>
using namespace std;
#define N 500005
#define ll long long
#define ri register int
const ll inf=1ll<<62;
ll ans=0,a[N];
int n,k,cho[N],l[N],r[N];
struct node{ int id; ll val; };
priority_queue<node> q;
bool operator < (const node &a,const node &b)//只能重载 < 小于号 
{
    return a.val<b.val;
}
int main()
{
    freopen("so.in","r",stdin);
    freopen("so.out","w",stdout);
    scanf("%d%d",&n,&k);
    node tmp;
    for(ri i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        tmp.id=i; tmp.val=a[i];
        l[i]=i-1; r[i]=i+1;//数组模拟链表 
        q.push(tmp);
    }
    //注意边界的初始化
    //初始化 a[0] 和 a[n+1]的原因:在a[now]=a[l[now]] + a[r[now]] - a[now];的时候 可能调用到a[0] 和 a[n+1]
    //然而这两个位置是不存在的 所以应该设成负无穷来防止加到他们重新加入队列中的值。 
    l[1]=0; r[n]=n+1; a[0]=-inf; a[n+1]=-inf;
    //-inf一定要负够 否则就会加到a[0] 和 a[n+1] 。 
    int cnt=0;
    while(!q.empty()){
        while(cho[q.top().id]) q.pop();
        node x=q.top(); q.pop();
        int now=x.id;
        if(cnt>=k) break;
        ans+=x.val;
        a[now]=a[l[now]] + a[r[now]] - a[now]; 
        cho[l[now]]=1; cho[r[now]]=1;
        l[now]=l[l[now]];  r[l[now]]=now;//画图理解链表删除的指向 
        r[now]=r[r[now]];  l[r[now]]=now;
        cnt++; 
        x.val=a[now],q.push(x);
    }
    printf("%lld\n",ans);
}
/*
6 3
100 1 -1 100 1 -1

3 2
4 10 3
17 9
4
6
8
1
5
8
2
7
9
7
4
8
5
9
4
1
9
*/
100分

注意:

代码很简单,注意初始化链表的边界!!!

有一道题基本和这道题一样:洛谷P1484 种树

#include<bits/stdc++.h>
using namespace std;
#define N 500005
#define ll long long
#define ri register int
ll ans=0,a[N];
int n,k,cho[N],l[N],r[N];
struct node{ int id; ll val; };
priority_queue<node> q;
bool operator < (const node &a,const node &b)
{
    return a.val<b.val;
}
int main()
{
    scanf("%d%d",&n,&k);
    node tmp;
    for(ri i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        tmp.id=i; tmp.val=a[i];
        l[i]=i-1; r[i]=i+1;
        q.push(tmp);
    }
    l[1]=0; r[n]=n+1;
    int cnt=0;
    while(!q.empty()){
        while(cho[q.top().id]) q.pop();
        node x=q.top(); q.pop();
        int now=x.id;
        if(x.val<0 || cnt>=k) break;
        ans+=x.val;
        a[now]=a[l[now]] + a[r[now]] - a[now];
        cho[l[now]]=1; cho[r[now]]=1;
        l[now]=l[l[now]];  r[l[now]]=now;
        r[now]=r[r[now]];  l[r[now]]=now;
        cnt++; 
        x.val=a[now],q.push(x);
    }
    printf("%lld\n",ans);
}
/*
6 3
100 1 -1 100 1 -1

3 2
4 5 3

17 9
4
6
8
1
5
8
2
7
9
7
4
8
5
9
4
1
9
0
*/
种树

 

posted on 2019-09-16 15:28  rua-rua-rua  阅读(186)  评论(0编辑  收藏  举报