K-periodic Garland(思維DP)(Codeforces Round #642 (Div. 3) E題)

在這裏插入圖片描述

題目大意

       給你一個長度爲n(n106)n(n \le 10^6)0101串,每一次你可以使得其中一個位置的狀態翻轉,要求使得裏面的每一個11之間的距離爲kk,問至少需要多少次操作?

分析過程

       這個DPDP還是挺好想的,發現自己的思路和標程不太一樣,所以寫篇博客記錄一下。
       我們定義dp[i][j]dp[i][j]表示爲截止到ii位置(且ii之前的序列已滿足kk週期),其狀態爲j(j0,1)j(j \in{0,1})時的最優解;我們再定義sum[i]sum[i]爲前綴和數組。
j==0j==0,那麼此狀態可由i1i-1的最優狀態轉移而來,當前狀態如果不爲00需要再加一個貢獻,有狀態轉移方程
dp[i][0]=min(dp[i1][0],dp[i1][1])+[a[i]==1]dp[i][0]=min(dp[i-1][0],dp[i-1][1])+[a[i]==1]
(其中[]內的值爲真則爲1,否則爲0)

j==1j==1,這個時候需要區間(ik,k)(i-k,k)內全部爲00,所以需要將區間內sum[i1]sum[ik]sum[i-1]-sum[i-k]11狀態進行翻轉,然後對於區間[1,ik][1,i-k]的狀態有兩種情形:一種是該區間的狀態全部爲00,這個時候需要進行sum[ik]sum[i-k]次調整;另一種是該區間也滿足最優結構,對於兩種情形,我們取min(sum[ik],dp[ik][1])min(sum[i-k],dp[i-k][1]),所以有狀態轉移方程dp[i][1]=min(sum[ik],dp[ik][1])+[a[i]==0]+sum[i1]sum[ik]dp[i][1]=min(sum[i - k], dp[i - k][1])+[a[i]==0]+sum[i-1]-sum[i-k]

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章