題目連結
https://www.luogu.com.cn/problem/AT_pakencamp_2019_day3_d
題意簡述
給定 \(5\) 行 \(N\) 列的網格,每個格子有四種可能的顏色。
要使網格中每一列的顏色都一樣但不能是黑色,並且相鄰兩列的顏色不相同。問最少改變多少個格子的顏色能滿足要求。
思路分析
為方便處理,把給定的紅色、藍色、白色、黑色分別轉成數字 \(1,2,3,4\)。
考慮 dp。
不妨設 \(c_{i,j}\) 為將第 \(i\) 列全部變成第 \(j\) 種顏色的代價,\(f_{i,j}\) 表示第 \(i\) 列變成第 \(j\) 種顏色時前 \(i\) 列的總代價。
那麼思路就很簡單了。對於當前列的當前顏色,其總代價應為前一列不同顏色的總代價的最小值與將當前列轉變為當前顏色的代價的和,即狀態轉移方程為 \(f_{i,j}=\min(f_{i-1,k})+c_{i,j}(j \ne k)\),最後在最後一列的幾種顏色中取最小值即可。
細節內容見程式碼註釋。
程式碼
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1005;
int n,ans=0x3f3f3f3f,mp[10][N],c[N][5],f[N][5];//mp表示原矩陣,c和f如上文
//將字元轉為數字
int change(char ch){
switch(ch){
case 'R':return 1;break;
case 'B':return 2;break;
case 'W':return 3;break;
case '#':return 4;break;
}
}
int main(){
cin>>n;
for(int i=1;i<=5;++i)
for(int j=1;j<=n;++j){
char ch;cin>>ch;
mp[i][j]=change(ch);
}
//預處理代價
for(int i=1;i<=n;++i)//列舉列
for(int j=1;j<=3;++j)//列舉顏色,因為不能是黑色,所以只列舉三種
for(int k=1;k<=5;++k){
if(mp[k][i]==j)continue;
++c[i][j];
}
for(int i=1;i<=n;++i){
for(int j=1;j<=3;++j){
f[i][j]=0x3f3f3f3f;
for(int k=1;k<=3;++k){
if(j==k)continue;//相鄰兩列顏色不能一樣
f[i][j]=min(f[i][j],f[i-1][k]+c[i][j]);//狀態轉移
}
}
}
//在最後一列的幾種顏色中取最小值
for(int i=1;i<=3;++i)ans=min(ans,f[n][i]);
cout<<ans;
return 0;
}
時間複雜度分析
用 \(N\) 表示網格列數,\(M\) 表示網格行數,\(K\) 表示顏色數量,則預處理的時間複雜度為 \(O(NMK)\),dp 時間複雜度為 \(O(NK^{2})\)。本題中 \(M\) 和 \(K\) 都為常數,故時間複雜度較低,透過此題沒有任何問題。