鬼知道这破题是要写while (scanf("%d%d",&n,&m))啊。。。调了半天 气死我了
首先这道题朴素的写法是非常显然的。由于只有最近的两次涂色具有后效性,可以得出如下状态:
代表涂第i个且上一个涂的是第j个的最小代价
它是由的最小值转移过来的
但是如果未经任何优化的话空间复杂度为,时间复杂度也为(跑不满),基本是不太可能过去的。(至少我写t了)
然后考虑优化。
空间优化很好想。对于每个我们只需要第一维为的dp值,而,所以可以取模滚动。另外一种方式是将转化为,这样空间复杂度就可以降到 (SCU会卡空间,所以这个优化是必须有的
我们发现取最小值的操作有很大一部分是重复的。需要取最小值的dp值之比多一个,因此状态转移方程可以写成
因此我们可以从小到大循环第二个维度,从大到小循环第一个维度,同时维护目前为止f的最小值,实现O(1)转移
优化后的时间复杂度同样也可以降到
然后就可以很放心的写了
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define fi first
#define se second
#define ll long long
#define pq priority_queue
#define mp make_pair
#define pii pair<int,int>
#define mod 998244353
#define debug(x) cerr<<#x<<"="<<x<<'\n'
int lowbit(int x) {return x&(-x);}
int n,m;
int f[10010][110];
int c[10010];
int g[10010][110];
const int INF=1e9;
int main(){
while (scanf("%d%d",&n,&m)!=EOF){
for (int i=0;i<n;i++) scanf("%d",&c[i]);
memset (f, 0x3f3f3f3f, sizeof f);
int ans=0x3f3f3f3f;
for (int j=0;j<n;j++) {
int tmp=0x3f3f3f3f;
for (int k=min(m-1,n-j-1);k>0;k--) {
int i=j+k;//i>j
if (i<m) {
f[k][j]=c[i]+c[j];
continue;
}
tmp=min(tmp,f[j-i+m][i-m]); //f[j][i-m]
f[k][j]=tmp+c[i];
}
}
for (int i=n-m;i<n;i++)
for (int j=1;j<=m&&i+j<n;j++)
ans=min(ans,f[j][i]);
cout<<ans<<endl;
}
return 0;
}