一、题目
来源不详,好像是个外国网站。
peterpeterpeter 把 nnn 个白球排成一列,他想把一些白球刷为黑色,且任意连续 mmm 个球中至少要有 222 个黑球,peterpeterpeter 知道他需要 CiC_iCi? 的燃料刷第 iii个球,你的任务是找出 peterpeterpeter 所需的最少的燃料达到目标。
2≤n≤104,2≤m≤100,m≤n2\leq n\leq10^4,2\leq m\leq 100,m\leq n2≤n≤104,2≤m≤100,m≤n
二、解法
数据范围已经给出了很明显的提示了,设dp[i][j]dp[i][j]dp[i][j]为最后两个球染了iii和i?ji-ji?j,[1,i][1,i][1,i]全部合法的方案数,转移考虑让i?1i-1i?1这个位置结尾的mmm段合法,那么:
dp[i][j]=min?{dp[i?j][k]+a[i]}k∈[1,m?j]dp[i][j]=\min\{dp[i-j][k]+a[i]\}\space k\in[1,m-j]dp[i][j]=min{
dp[i?j][k]+a[i]} k∈[1,m?j]不难发现可以用前缀和优化。初始化我们要处理1≤i,j≤m1\leq i,j\leq m1≤i,j≤m的情况,如果状态dp[i][j]dp[i][j]dp[i][j]满足m?(i?j)<nm-(i-j)<nm?(i?j)<n就可以计入答案,时间复杂度O(nm)O(nm)O(nm)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 10005;
int read()
{
int num=0,flag=1;char c;while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();return num*flag;
}
int n,m,ans=1e9,a[M],dp[M][105],g[M][105];
signed main()
{
n=read();m=read();for(int i=1;i<=n;i++)a[i]=read();memset(g,0x3f,sizeof g);memset(dp,0x3f,sizeof dp);for(int i=1;i<=m;i++)for(int j=1;j<i;j++){
dp[i][j]=a[i]+a[i-j];g[i][j]=min(g[i][j-1],dp[i][j]);if(n-(i-j)<m) ans=min(ans,dp[i][j]);}for(int i=m+1;i<=n;i++)for(int j=1;j<m;j++)/*for(int k=1;k<=m-j;k++){dp[i][j]=min(dp[i][j],dp[i-j][k]+a[i]);if(n-(i-j)<m) ans=min(ans,dp[i][j]); }*/{
dp[i][j]=g[i-j][m-j]+a[i];g[i][j]=min(g[i][j-1],dp[i][j]);if(n-(i-j)<m) ans=min(ans,dp[i][j]);}printf("%d\n",ans);
}
/* i-j dp[i][j]=dp[i-j][ [1,m-j] ] + c[i] */