• 打工


    【问题描述】
    小S 是一个很萌很萌的勤劳能干的好孩子,他准备在暑假里去打工。小S 能够打工的
    天数总共为N,而在每一天可能领到不同的工资,其中第i 天打工能够领到的工资为A[i]
    元。小S 的精力是有限的,所以他决定在每个连续的K 天里至少休息一天(即小S 最长连
    续工作的天数不能大于等于K)。请你帮他安排在哪些天打工,使得他总共领到的工资最
    多。
    【输入】
    第一行为两个正整数N、K。
    第二行为N 个正整数A[i]。
    【输出】
    输出小S 能领到的最多的工资。
    【输入输出样例】
    work.in
    5 3
    1 2 3 1 4

    work.out
    9
    【数据范围】
    对于30%的数据:1 ≤ N ≤ 10。
    对于60%的数据:1 ≤ N ≤ 2501。
    对于100%的数据:1 ≤ K ≤ N ≤ 152501,1 ≤ A[i] ≤ 109。

    题解:

    【读题】

    这道题根据题意可以抽象出这个模型:给你一个序列总个数为n,

    你在其中每连续k个数都要选取一个数,问取出来的书的和最小是几

    最后再用总和减去这个数

    【样例解释】

    5 3
    1 2 3 1 4

    在第2、3、5 天打工,第1、4 天休息。

    【思路】

    这道题根据数据范围、题设等因素,很容易看出来是一道dp

    dp状态:dp[i]表示在a[i]必须取并满足条件时前i个数取得数的和最小是几

    dp初值:dp[0]=0,dp[i]=INF{1<=i<=n}

    dp转移:dp[i]=min{dp[j]}+a[i] (max(0,i-k)<=j<=i-1)

    dp答案:ans=min(dp[i]){n-k+1<=i<=n}

    最后输出sum(所有数的总和)-ans

    时间复杂度O(n*k)

    期望得分:60

    代码:

    #include<bits/stdc++.h>
    #define int long long 
    using namespace std;
    const int NR=152600;
    int dp[NR];
    int a[NR];
    int ans=1e9;
    int sum=0;
    signed main()
    {
        int n,k;
        scanf("%lld%lld",&n,&k);
        memset(dp,127,sizeof(dp));
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum+=a[i];
        dp[0]=0;
        for(int i=2;i<=n;i++)
        {
            for(int j=i-1;j>=max(i-k,0ll);j--)
            {
                dp[i]=min(dp[i],dp[j]+a[i]);
            }
        }
        for(int i=n;i>=n-k+1;i--)
        {
            ans=min(ans,dp[i]);
        }
        printf("%lld",sum-ans);
        return 0;
    }

    【优化】

    很明显,朴素的dp过不了这道题,所以需要用一下优化,

    而这道题在每个dp[i]转移时,都是一些dp[j]+a[i]去取最小值

    所以我们可以用一个线段树优化一下之前的代码,num[i]表示

    线段树第i个节点对应的叶节点中最小的数是几,至于不会线段

    树的同学们看一下这个网址:

    https://www.cnblogs.com/chen-1/p/11440708.html

    https://www.cnblogs.com/chen-1/p/11442635.html

    第一个是单点修改讲解,第二个是区间修改讲解

    时间复杂度O(n*logk)

    期望得分:100

    代码:

    #include<bits/stdc++.h>
    #define int long long 
    using namespace std;
    const int NR=152600;
    int dp[NR];
    int a[NR];
    int num[NR*4];
    int sum=0;
    void build(int l,int r,int root)
    {
        if(l==r) 
        {
            num[root]=dp[l];
            return;
        }
        int mid=(l+r)/2;
        build(l,mid,root*2);
        build(mid+1,r,root*2+1);
        num[root]=min(num[root*2],num[root*2+1]);
    }
    void change(int x,int val,int l,int r,int root)
    {
        if(l==r)
        {
            num[root]=val;
            return;
        }
        int mid=(l+r)/2;
        if(x<=mid) change(x,val,l,mid,root*2);
        if(x>mid) change(x,val,mid+1,r,root*2+1);
        num[root]=min(num[root*2],num[root*2+1]);
    }
    int search(int p,int q,int l,int r,int root)
    {
        if(p==l && q==r) return num[root];
        int mid=(l+r)/2;
        if(q<=mid) return search(p,q,l,mid,root*2);
        if(p>mid) return search(p,q,mid+1,r,root*2+1);
        return min(search(p,mid,l,mid,root*2),search(mid+1,q,mid+1,r,root*2+1));
    }
    signed main()
    {
        int n,k;
        scanf("%lld%lld",&n,&k);
        for(int i=1;i<=n;i++) dp[i]=1e16;
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]),sum+=a[i];
        dp[0]=0;
        dp[1]=a[1];
        build(1,n,1);
        for(int i=2;i<=n;i++)
        {
            dp[i]=search(max(i-k,1ll),i-1,1,n,1)+a[i];
            if(i-k<=0) dp[i]=min(dp[i],dp[0]+a[i]);
            change(i,dp[i],1,n,1);
        }
        printf("%lld",sum-search(n-k+1,n,1,n,1));
        return 0;
    }
    /*
    15 5
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    */
  • 相关阅读:
    前端- css
    前端- html -总结
    2016.9.15 黑客编程之无限启动
    2016.9.14 JavaScript入门之七面向对象和函数
    2016.9.13 JavaScript入门之六基础函数
    2016.9.1 JavaScript入门之五
    2016.9.9 网络工程师之路由器技术
    2016.8.14 网络工程师之网关协议
    2016.8.27 JavaScript入门之四
    2016.8.22 JavaScript入门之三
  • 原文地址:https://www.cnblogs.com/chen-1/p/11778529.html
Copyright © 2020-2023  润新知