打工

【问题描述】
小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
*/
原文地址:https://www.cnblogs.com/chen-1/p/11778529.html