題目大意
給你一個長度爲串,每一次你可以使得其中一個位置的狀態翻轉,要求使得裏面的每一個之間的距離爲,問至少需要多少次操作?
分析過程
這個還是挺好想的,發現自己的思路和標程不太一樣,所以寫篇博客記錄一下。
我們定義表示爲截止到位置(且之前的序列已滿足週期),其狀態爲時的最優解;我們再定義爲前綴和數組。
當時,那麼此狀態可由的最優狀態轉移而來,當前狀態如果不爲需要再加一個貢獻,有狀態轉移方程
(其中[]內的值爲真則爲1,否則爲0)
當時,這個時候需要區間內全部爲,所以需要將區間內個狀態進行翻轉,然後對於區間的狀態有兩種情形:一種是該區間的狀態全部爲,這個時候需要進行次調整;另一種是該區間也滿足最優結構,對於兩種情形,我們取,所以有狀態轉移方程
Another Solution
看到網上有一個更加巧妙的思路。參考地址
AC代碼
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 100;
typedef long long ll;
int n, k, sum[maxn], a[maxn], dp[maxn][2];
void solve(){
int i, j, ans;
for(i=1;i<=k;++i){
dp[i][1] = (a[i] == 0) + sum[i - 1];
}
for(i=1;i<=n;++i){
if(i - k >= 1){
dp[i][1] = (a[i] == 0) + sum[i - 1] - sum[i - k];
dp[i][1] += min(sum[i - k], dp[i - k][1]);
}
dp[i][0] = min(dp[i - 1][0], dp[i - 1][1]) + (a[i] == 1);
}
ans = min(dp[n][0], dp[n][1]);
cout<<ans<<'\n';
}
int main(){
int t, i, j;
char ch;
ios::sync_with_stdio(false);
cin>>t;
while(t--){
cin>>n>>k;
cin.get();
for(i=1;i<=n;++i){
ch = cin.get();
if(ch == '0'){
a[i] = 0;
}else{
a[i] = 1;
}
sum[i] = sum[i-1] + a[i];
}
cin.get();
solve();
}
return 0;
}
Another Solution AC代碼
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
char s[N];
signed main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while(t --)
{
int n , k , sum = 0 , ans = 0x3f3f3f3f;
cin >> n >> k >> s + 1;
for(int i = 1 ; i <= n ; i ++) sum += s[i] - '0';
for(int i = 1 ; i <= k ; i ++)
{
int cnt = 0;
for(int j = i ; j <= n ; j +=k)
{
if(s[j] == '1') cnt -- ;
else cnt ++ ;
cnt = min(cnt , 0);
ans = min(ans , sum + cnt);
}
}
cout << ans << '\n' ;
}
return 0;
}