[CF1450C2]Errich-Tac-Toe (Hard Version)
阿新 • • 發佈:2021-07-16
壹、題目描述 ¶
貳、題解 ¶
飛雪連天射 \(\rm C1\),卻見彎弓跪 \(\rm C2\),自 \(\rm C1\) 使用一種奇怪的旋轉填法過掉後,我始終認為 \(\rm C2\) 也是可以這樣做的,然而事實是
然而我將剩下的所有事件都拿來肝 \(D\),還是差點時間優化......
事實上,我的 \(\rm C1\) 做法並不能拓展到 \(\rm C2\),想要做 \(\rm C2\) 還需另尋他路。
這種問題,顯然是要對這個格子圖染色,結合經典的做法,我們對於 \((i+j)\bmod 3\) 的餘數入手進行染色,然後我們可以得到這樣的圖:
如果有這樣一張表:
\(\rm Character\)\\((i+j)\bmod 3\) | \(0\) | \(1\) | \(2\) |
---|---|---|---|
\(X\) | \(x_0\) | \(x_1\) | \(x_2\) |
\(O\) | \(o_0\) | \(o_1\) | \(o_2\) |
那首先有 \(k=x_0+x_1+x_2+o_0+o_1+o_2\). 如果令 \(a_{ij}=x_i+o_j\),那麼就有
\[a_{01}+a_{02}+a_{10}+a_{12}+a_{20}+a_{21}=2k \]顯然,就會存在
\[\min\{a_{01},a_{02},a_{10},a_{12},a_{20},a_{21}\}\le \left\lfloor{2k\over 6}\right\rfloor \]找到最小值,並替換即可。不過這真的是人類智慧嗎......我並不覺得這方法很容易想到。
叄、參考程式碼 ¶
#include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; // #define NDEBUG #include<cassert> namespace Elaina{ #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i) #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i) #define fi first #define se second #define mp(a, b) make_pair(a, b) #define Endl putchar('\n') #define mmset(a, b) memset(a, b, sizeof a) // #define int long long typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; typedef pair<ll, ll> pll; template<class T>inline T fab(T x){ return x<0? -x: x; } template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); } template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); } template<class T>inline T readin(T x){ x=0; int f=0; char c; while((c=getchar())<'0' || '9'<c) if(c=='-') f=1; for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48)); return f? -x: x; } template<class T>inline void writc(T x, char s='\n'){ static int fwri_sta[1005], fwri_ed=0; if(x<0) putchar('-'), x=-x; do fwri_sta[++fwri_ed]=x%10, x/=10; while(x); while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed); putchar(s); } } using namespace Elaina; const int maxn=300; char c[maxn+5][maxn+5]; int n; int a[5][5]; inline void geta(){ memset(a, 0, sizeof a); for(int i=0; i<n; ++i){ for(int j=0; j<n; ++j) if(c[i][j]!='.'){ int cur=(i+j)%3; if(c[i][j]=='X') rep(t, 0, 2) ++a[cur][t]; else rep(t, 0, 2) ++a[t][cur]; } } } signed main(){ rep(_, 1, readin(1)){ n=readin(1); for(int i=0; i<n; ++i) scanf("%s", c[i]); geta(); int minn=n*n, x, y; rep(i, 0, 2) rep(j, 0, 2) if(i!=j){ if(a[i][j]<minn) minn=a[i][j], x=i, y=j; } for(int i=0; i<n; ++i) for(int j=0; j<n; ++j) if(c[i][j]!='.'){ int cur=(i+j)%3; /** fill in the opposite color */ if(cur==x) c[i][j]='O'; else if(cur==y) c[i][j]='X'; } for(int i=0; i<n; ++i) printf("%s\n", c[i]); } return 0; }
肆、關鍵之處 ¶
這個構造方法......只能說 “限制可能會給人以提示” 了罷......另外可能就只有 \((i+j)\bmod 3\) 的餘數這方面可以想到了。