最近做了多少道 usaco 了,连 FJ 都认识我了呀
给你 \(N\) 段时间其中 \(B\) 段时间你要用来睡眠,再给你每个时间睡眠可获得的效用值 \(U_i\)。
可惜的是你每次睡眠的第一段时间都要用来入睡(安眠药它不香吗)。
求你可以获得的最大效用值。
一眼看上去就是 DP 啊。
定义 \(f(i,j,1/0)\) 表示:在第 \(i\) 段时间,已近休息了 \(j\) 段时间,此时是否休息。
假设没有循环时间,那么状态转移方程为:
\(f(i,j,1)=max(f(i-1,j-1,0),f(i-1,j-1,1)+u_i)\)
\(f(i,j,0)=max(f(i-1,j,0),f(i-1,j,1))\)
\(f(i,0,0)=f(1,1,1)=0(1\leq i\leq n),f(i,j,0/1)=-INF(2\leq i,j\leq n)\)
那么如果没有时间阶段是不断循环的圆
这句话这道题就没了。
那么有的话怎么办办呢?(暗杀良心出题人同志并把这句话删了)
我的思路是枚举每一段时间为起点,做一次 DP,默默算算时间(\(O(n^3)\))后自闭。
后来老师说其实只需要两次 DP:
然后就没了。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iostream>
#define N 3831
using namespace std;
int n,m,a[N],f[N][N][2],ans=0;
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
memset(f,0xcf,sizeof(f));
f[1][1][1]=0;
for(int i=1;i<=n;i++) f[i][0][0]=0;
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++){
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+a[i]);
}
ans=max(f[n][m][0],f[n][m][1]);
memset(f,0xcf,sizeof(f));
f[1][1][1]=a[1];
for(int i=1;i<=n;i++) f[i][0][0]=0;
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++){
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+a[i]);
}
ans=max(ans,f[n][m][1]);
printf("%d\n",ans);
return 0;
}
完结撒花。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章