SCU-3073 Painting the balls

鬼知道这破题是要写while (scanf("%d%d",&n,&m))啊。。。调了半天 气死我了

首先这道题朴素的写法是非常显然的。由于只有最近的两次涂色具有后效性,可以得出如下状态:

f[i][j]代表涂第i个且上一个涂的是第j个的最小代价

它是由f[j][i-m,...,j-1]的最小值转移过来的

但是如果未经任何优化的话空间复杂度为O(n^{^2}),时间复杂度也为O(nm^{2})(跑不满),基本是不太可能过去的。(至少我写t了)

然后考虑优化。

空间优化很好想。对于每个f[i][j]我们只需要第一维为j的dp值,而i-j<=m,所以可以取模滚动。另外一种方式是将f[i][j]转化为f[i][i-j],这样空间复杂度就可以降到O(nm) (SCU会卡空间,所以这个优化是必须有的

我们发现取最小值的操作有很大一部分是重复的。f[i][j]需要取最小值的dp值之比f[i+1][j]多一个f[j][i-m],因此状态转移方程可以写成

f[i][j]=min(f[i+1][j],f[j][i-m])

因此我们可以从小到大循环第二个维度,从大到小循环第一个维度,同时维护目前为止f的最小值,实现O(1)转移

优化后的时间复杂度同样也可以降到O(nm)

然后就可以很放心的写了

#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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值