F. Alice and Recoloring 1&2
阿新 • • 發佈:2021-10-04
題意:給定一個 n * m 的 0 1 矩陣,你需要使用以下四種操作使得個矩陣全為 0 ,求出最小的代價。
- 代價為 1 的第一種操作:反轉矩陣的某個左上子矩陣。
- 代價為 2 的第二種操作:反轉矩陣的某個左下子矩陣。
- 代價為 4 的第三種操作:反轉矩陣的某個右上子矩陣。
- 代價為 3 的第四種操作:反轉矩陣的某個右下子矩陣。
分析:
- 首先需要發現第二種和第三種操作是可以被第一種操作替代的,且代價不會更高。
- 將原先的矩陣做差分。原先對關於(x,y)的子矩陣的操作,對於差分後的矩陣,第一種操作等價於反轉(x,y)一個單元,第四種操作相當於反轉(x-1,y-1),(x-1,m),(n,y-1),(n,m)這四個單元。
- 考慮第四種操作什麼時候是有意義的,顯現需要這個操作使得四個單元都由 0 變成 1 。又由於它們都是關於(n,m)的,所以該操作最多執行一次。
#include<bits/stdc++.h> #define ll long long #define ls u<<1 #define rs u<<1|1 #define mm(x) memset(x,0,sizeof(x)) #define debug(x) cout << #x << ":" << x << '\n' using namespace std; intView Coderead() { int a=0;int f=0;char p=getchar(); while(!isdigit(p)){f|=p=='-';p=getchar();} while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();} return f?-a:a; } const int INF=998244353; int T; int n,m; int ans; char s[550][550]; bool val[550][550]; int main() { n=read(); m=read();for(int i=1;i<=n;++i) scanf("%s",s[i]+1); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) val[i][j]=s[i][j]=='B'; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) val[i][j]^=val[i+1][j]^val[i][j+1]^val[i+1][j+1]; ans=val[n][m]; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { if(i==n&&j==m) continue; if(val[i][j]) { ans++; if(i==n) continue; if(j==m) continue; if(val[i][m]&&val[n][j]&&val[n][m]) { ans--; val[n][m]=false; } } } printf("%d",ans); return 0; }
題意:給定一個 n * m 的 0 1 矩陣,你需要使用以下四種操作使得個矩陣全為 0 ,求出最小的代價。
- 代價為 1 的第一種操作:反轉矩陣的某個左上子矩陣。
- 代價為 2 的第二種操作:反轉矩陣的某個左下子矩陣。
- 代價為 4 的第三種操作:反轉矩陣的某個右上子矩陣。
- 代價為 2 的第四種操作:反轉矩陣的某個右下子矩陣。
分析:相比於前一題,第四種操作的代價變小了。造成的結果是,第四種操作能執行多次。
- 依舊是首先將原先的矩陣做差分。
- 考慮第四種操作作用的點。假設對於同一行或者同一列的兩個單元執行第四種操作,那麼我們相當於花費了 4 的代價反轉了 4 個單元。並不比第一種操作更加節約,因此我們避免這種操作。
- 繼續考慮第四種操作,如果個操作影響的四個單元中為 1 的個數少於或者等於三個,考慮到要把至少一個單元反轉回來,顯然也不會更節約。
- 由於(n,m)是所有第四種操作都會影響的單元,我們暫時先不考慮它。我們考慮是否要對一個單元(x,y)執行第四種操作時,首先保證(x,y),(x,m),(n,y)都為 1 ,將它加入備選佇列。
- 接下來我們需要的是在備選佇列中選出儘可能多的點,滿足沒有任意兩個點的 X 或者 Y 相等(這是一個經典的關於網格的二分圖匹配問題)。
#include<bits/stdc++.h> #define ll long long #define ls u<<1 #define rs u<<1|1 #define mm(x) memset(x,0,sizeof(x)) #define debug(x) cout << #x << ":" << x << '\n' using namespace std; int read() { int a=0;int f=0;char p=getchar(); while(!isdigit(p)){f|=p=='-';p=getchar();} while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();} return f?-a:a; } const int INF=998244353; int T; int n,m; int ans,tot; char s[550][550]; bool val[550][550]; bool link[550][550]; int mat[550]; int vis[550]; bool dfs(int u,int t) { for(int i=1;i<=m;++i) { if(link[u][i]&&vis[i]!=t) { vis[i]=t; if(!mat[i]||dfs(mat[i],t)) { mat[i]=u; return true; } } } return false; } int main() { n=read(); m=read(); for(int i=1;i<=n;++i) scanf("%s",s[i]+1); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) val[i][j]=s[i][j]=='B'; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { val[i][j]^=val[i+1][j]^val[i][j+1]^val[i+1][j+1]; ans+=val[i][j]; } for(int i=1;i<n;++i) for(int j=1;j<m;++j) if(val[i][j]&&val[i][m]&&val[n][j]) link[i][j]=true; for(int i=1;i<=n;++i) if(dfs(i,i)) ++tot; ans-=val[n][m]; ans-=tot; if((tot^val[n][m])&1) ans++; printf("%d",ans); return 0; }View Code