【状态设计优化DP】Atcoder Beginner Contest E - Work or Rest
妈的,补完还是感觉很神秘啊
我发现我DP真的就只会写入门题呜呜呜
但是群友说abcA-G都是签到
那我大约连入门题都不会吧QwQ
题意:
一周有n天,每天除了休假就是工作,对于工作日的贡献是Amin(x,y),其中x和y是离前一个假日和后一个假日的距离,问最大贡献加起来是多少,注意哪天是休假哪天是工作是不确定的
思路:
一眼DP
一开始是这样想的:
因为哪天工作不确定,因此直接设dp[i][1]表示工作日的贡献,dp[i][0]是休假日的贡献
其实休假日并没有贡献,所以干脆直接设dp[i]为工作日i的贡献,答案就是求和
然后就不懂啊,怎么转移,因为前面和后面第一个休假日都不确定,那就去枚举两个指针?因为不止一周,怎么去处理,倍增数组吗?
然后就不会了
正解是设dp[i]为前i天且第i天休假的贡献和
感觉这个状态设的很神秘啊,但是仔细想想就会发现很妙,这样枚举一个指针就可以了
看完觉得很难受,这是咋想到的
当状态设计后发现转移的复杂度太高时,试着在状态设计的时候确定一个量,再去转移
然后去考虑转移,转移的时候考虑贡献是怎么算的
枚举完l后就得加上中间工作日的贡献
怎么O(1)算贡献?前缀和就好了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mxn=5e3+10;
int n;
int a[mxn],dp[mxn],sum[mxn];
int cost(int l,int r){
int len=r-l-1;
return sum[(len+1)/2]+sum[len/2];
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],sum[i]=sum[i-1]+a[i];
memset(dp,128,sizeof(dp));
dp[1]=0;
for(int i=2;i<=n+1;i++){
for(int j=1;j<i;j++) dp[i]=max(dp[i],dp[j]+cost(j,i));
}
cout<<dp[n+1]<<'\n';
}
总结:
当状态设计后发现转移的复杂度太高时,试着在状态设计的时候确定一个量,再去转移