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