题目描述
小宝把(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;
}