题目链接(别想了)
小H的硬币游戏
题目描述
小H参加了一场神秘的游戏。游戏中有\(n\)堆硬币,第\(i\)堆价值\(a_i\)。每次小H可以选择编号相差\(k\)的硬币同时拿走。注意拿走后硬币不进行重标号。小H想知道最多能拿走多大价值的硬币。
输入格式:
第一行两个整数\(n\),\(k\)。
第二行\(n\)个整数。第\(i\)个整数表示\(a_i\)。
输出格式:
一行一个整数,表示拿走硬币的最大价值。
样例输入:
7 3 7 5 8 6 4 3 2
样例输出:
33
数据规模:
对于\(20%\)的数据,\(n<=20\)。
对于\(40%\)的数据,\(n<=2000\)。
对于另外\(20%\)的数据,\(k<=10\)。
对于\(100%\)的数据,\(n<=100000\),\(k<=n\),\(ai<=1000000000\)。
时间限制
1s
内存限制
256M
题解
题目要求给\(n\)个数,每对序号相差\(k\)的数可以被取走,求取走的数的和最大为多少。
因为每次取的数对序号相差必须为\(k\),所以如果一个数的序号为\(x\),那么对这个数有影响的数的序号为\(a*k+x%k\)。
那么我们可以把这\(n\)个数根据它们的序号\(modk\)的值分成\(k\)个集合,只有集合内的数才能互相影响。
接下来直接\(dp\)就行了。
\(dp[j]\)表示前\(k\)个数在\(k\)所在的集合内所能取到的最大值为多少。
转移方程为:\(dp[j]=max(dp[j-k],dp[j-2*k]+a[j-k]+a[j])\)
当\(k<j<=2*k\)时,\(dp[j]=a[j-k]+a[j]\)。
最后在\(j+k>n\)时(这个集合内的最后一个元素加入集合后)把\(dp[j]\)加到\(ans\)里面去。
上代码:
#include<bits/stdc++.h> using namespace std; int n,k; long long a[100009]; long long ans,dp[100009]; int main(){ scanf("%d%d",&n,&k); for(int j=1;j<=n;j++) scanf("%lld",&a[j]); for(int j=1;j<=n;j++){ if(j-k>0 && j<=2*k) dp[j]=a[j-k]+a[j]; if(j-2*k>0) dp[j]=max(dp[j-k],dp[j-2*k]+a[j-k]+a[j]); if(j+k>n) ans+=dp[j]; } printf("%lld",ans); return 0; }