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;
}
posted @ 2019-08-04 15:06  dsjkafdsaf  阅读(467)  评论(0编辑  收藏  举报