1. 程式人生 > 實用技巧 >CF1391D 505(狀壓dp+思維)

CF1391D 505(狀壓dp+思維)

觀察到這個資料範圍,顯然不可能真的存這麼大的鄰接矩陣。

進一步發現,題目要求正方形矩陣的邊長為偶數,並且裡面的1為奇數

這啟發了我們,因為邊長為4的一定要用邊長為2的拼接起來,這樣邊長為4的裡面一定是偶數個1

因此直接特判掉n>=4的答案

剩下只有n==2,和n==3。

對於n==2,我們發現因為每列只能是奇偶奇偶,或者是偶奇偶奇。因此我們就判斷這兩種情況取一個最小值

對於n==3,可以發現這是經典的網格圖狀壓,也就是玉米田問題,因為種類只有8,我們先計算不衝突的情況,之後對於列舉每一列,求出合法狀態

最終的答案就是每種狀態取min

#include<bits/stdc++.h>
using
namespace std; typedef long long ll; typedef pair<int,int> pll; typedef pair<int,pll> plll; const int N=5e5+10; const int mod=1e9+7; char s[5][N]; int f[N][16]; vector<int> g[N]; //判斷是否成立 bool check(int a,int b){ int a1=(a>>0)&1,b1=(b>>0)&1; int a2=(a>>1
)&1,b2=(b>>1)&1; int a3=(a>>2)&1,b3=(b>>2)&1; if((a1+a2+b1+b2)%2==0) return false; if((a2+a3+b2+b3)%2==0) return false; return true; } //計算交換代價 int cal(int a,int b){ int a1=s[1][a],b1=(b>>0)&1; int a2=s[2][a],b2=(b>>1)&1
; int a3=s[3][a],b3=(b>>2)&1; int ans=0; if(a1!=b1) ans++; if(a2!=b2) ans++; if(a3!=b3) ans++; return ans; } int main(){ ios::sync_with_stdio(false); int n,m; cin>>n>>m; int i,j; if(n==1){ cout<<0<<endl; } else if(n>=4){ cout<<-1<<endl; } else{ int i,j; for(i=1;i<=n;i++){ for(j=1;j<=m;j++){ char c; cin>>c; s[i][j]=c-'0'; } } if(n==2){ int ans1=0,ans2=0; for(i=1;i<=m;i++){ int tmp=i&1; if(s[1][i]^s[2][i]!=tmp) ans1++; } for(i=1;i<=m;i++){ int tmp=(i+1)&1; if(s[1][i]^s[2][i]!=tmp) ans2++; } cout<<min(ans1,ans2)<<endl; } else{ for(i=0;i<8;i++){ for(j=0;j<8;j++){ if(check(i,j)) g[i].push_back(j); } } for(i=0;i<8;i++){ f[1][i]=cal(1,i); } for(i=2;i<=m;i++){ for(j=0;j<8;j++){ f[i][j]=1e9; for(auto x:g[j]){ f[i][j]=min(f[i][j],f[i-1][x]+cal(i,j)); } } } int ans=1e9; for(i=0;i<8;i++){ ans=min(ans,f[m][i]); } cout<<ans<<endl; } } return 0; }
View Code