zoukankan      html  css  js  c++  java
  • 开学考试题4:so 就(优先队列+贪心)

    题目:

    【背景描述】

    一排 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
    "); 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("
    ");
        }
         
        printf("0:%lld 1:%lld
    ",dp[n][k][0],dp[n][k][1]);*/
        printf("%lld
    ",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
    ",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
    ",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
    */
    种树
  • 相关阅读:
    作业12:字典dict讲解及增删改查等操作
    作业11:元祖及元祖的嵌套
    作业10:列表的嵌套
    作业09:列表的增删改查
    什么数据类型
    作业08:字符串操作
    Visual Studio Code 写Python 代码
    Python——面向对象(初级篇)
    Python 学习第三部分函数——第一章函数基础
    Python3 字典
  • 原文地址:https://www.cnblogs.com/mowanying/p/11527537.html
Copyright © 2011-2022 走看看