SGU 183 Painting the balls

题目描述

小宝把(N)个白球排成一列,他想把一些白球刷为黑色,且任意连续(m)个球中至少要有(2)个黑球。

小宝知道他需要(C_i)的染料刷第(i)个球。请你帮小宝算算他最少需要多少染料。

Input

第一行两个整数(n)(m)

第二行(n)个整数,表示(C_i)

对于(30\%)的数据(n)的数据范围([1,20])

对于(60\%)的数据(n)的数据范围([1,500]),(m)的范围([2,100]);

对于(80\%)的数据(n)的数据范围([1,10000]),(m)的范围([2,1000]);

对于(100\%)的数据(n)的数据范围([1,20000]),(m)的范围([2,2000]),(m≤n),(C_i)的范围([1,20000]);

Output

输出最少需要的染料数量。

Sample Input

6 3
1 5 6 2 1 3

Sample Output

9

可以发现对于一个状态到下一个状态进行转移时,只用关心最后两个被染色的位置,(注意不是一个)

这样我们可以非常方便的打出时间复杂度为(O(n*n*m))的代码。

(dp[i][j])表示前(j)个球满足条件,最后的球在(j)(i)上的最小代价。

那么(dp[i][k])就可以由(dp[j][i])转移过来,其中由于连续(m)个球必须要有(2)个黑球所以(j)必须要在([k-m,i-1])的区间内才行。

(dp[i][j]=min(dp[k][i]+A[j]),j-m le k le i-1;)

由此可以过掉(60\%)的数据。

代码如下

#include <bits/stdc++.h>

using namespace std;

#define u64 unsigned long long
#define u32 unsigned int
#define reg register
#define Raed Read
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(reg int i=(G).Head[x]; i; i=(G).Nxt[i])

inline int Read() {
	int res  = 0, f = 1;
	char c;
	while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
	do res = (res << 3) + (res << 1) + (c ^ 48);
	while (c = getchar(), c >= 48 && c <= 57);
	return f ? res : -res;
}

template<class T>inline bool Min(T &a, T const&b) {
	return a > b ? a = b, 1 : 0;
}
template<class T>inline bool Max(T &a, T const&b) {
	return a < b ? a = b, 1 : 0;
}

const int N=2e4+5,M=1e5+5,mod=205;

bool MOP1;

int n,m,A[N];

struct T260 {
	int dp[505][505];
	inline void solve(void) {
		memset(dp,63,sizeof dp);
		rep(i,1,m)rep(j,i+1,m)dp[i][j]=A[i]+A[j];
		rep(i,m+1,n) {
			int L=i-m,R=i-1;
			rep(j,L,R)rep(k,j+1,R)Min(dp[k][i],dp[j][k]+A[i]);
		}
		int L=n-m+1,R=n,Ans=1e9;
		rep(i,L,R)rep(j,i+1,R)Min(Ans,dp[i][j]);
		printf("%d",Ans);
	}
} P60;

bool MOP2;

inline void _main(void) {
	n=Raed(),m=Read();
	rep(i,1,n)A[i]=Read();
	P60.solve();
}

signed main() {
#define offline1
#ifdef offline
	freopen("paint.in", "r", stdin);
	freopen("paint.out", "w", stdout);
	_main();
	fclose(stdin);
	fclose(stdout);
#else
	_main();
#endif
	return 0;
}

对于相同的(i),如果对(j)由大到小枚举,会有重叠的部分,可以直接以(O(1))的复杂度得到(dp[k][i]),从而把复杂度降到了(O(M*N))

可是我们又发现,但我们沿用上面的(dp)定义时,空间已经不够我们开了。

而倒数第二个球又一定在倒数第一个球前(m)个以内。

因此,我们只用把(dp)状态改为(dp[i][j])表示最后一个为(i),倒数第二个位距离(i)(j)的最小代价。

可是,最麻烦的事情来了,空间还是不够!!!

因此,我们可以用滚动数组进行优化,由于对于当前的(dp[i][j]),有用的只有([i-m+1,i-1])范围内的(dp)值。

因此,我们可以把第一维压成(m),对(i)进行取模节省空间。

代码如下

#include <bits/stdc++.h>

using namespace std;

#define u64 unsigned long long
#define u32 unsigned int
#define reg register
#define Raed Read
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(reg int i=(G).Head[x]; i; i=(G).Nxt[i])

inline int Read() {
	int res  = 0, f = 1;
	char c;
	while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
	do res = (res << 3) + (res << 1) + (c ^ 48);
	while (c = getchar(), c >= 48 && c <= 57);
	return f ? res : -res;
}

template<class T>inline bool Min(T &a, T const&b) {
	return a > b ? a = b, 1 : 0;
}
template<class T>inline bool Max(T &a, T const&b) {
	return a < b ? a = b, 1 : 0;
}

const int N=2e4+5,M=1e5+5,mod=205;

bool MOP1;

int n,m,A[N];

struct T2100 {
	int dp[2005][2005],Mod[N];
	inline void solve(void) {
		int Ans=1e9;
		rep(i,1,n)Mod[i]=i%m;
		rep(i,1,n) {
			memset(dp[Mod[i]],63,sizeof dp[Mod[i]]);
			rep(j,max(0,i-m+1),i-1)Min(dp[Mod[i]][j+m-i],dp[Mod[j]][i-j]+A[i]);
			drep(j,m,0)Min(dp[Mod[i]][j],dp[Mod[i]][j+1]);
			if(i>=n-m+1)Min(Ans,dp[Mod[i]][n-i]);
		}
		printf("%d",Ans);
	}
} P100;

bool MOP2;

inline void _main(void) {
	n=Raed(),m=Read();
	rep(i,1,n)A[i]=Read();
	P100.solve();
}

signed main() {
#define offline1
#ifdef offline
	freopen("paint.in", "r", stdin);
	freopen("paint.out", "w", stdout);
	_main();
	fclose(stdin);
	fclose(stdout);
#else
	_main();
#endif
	return 0;
}
原文地址:https://www.cnblogs.com/dsjkafdsaf/p/11298285.html