中秋假期乱做

P4363 [九省联考 2018] 一双木棋 chess

晚自修想了十几 min,胡了一个做法。

考虑它这个 n,m 很小,猜测可能上到指数级别。考虑先分析放的操作,不难发现几个性质。

  1. (i,j) 能够放置,当且仅当 (i,j) 为空,且 (1,1),(i,j) 这个矩形除了 (i,j) 都已放置。证明考虑要放置 (i,j),则 (x,j),(i,y) 都应该放置,而这些要放置则又是约束,可看成横竖 2 条直线的限制。

  2. 每个时刻的放置状态一定呈倒三角阶梯状。即对于每列的行数单调不增。证明考虑下性质 1 即可反证。

那么,这样暴力还是会有 1010 状态数。

但是考虑 dp 转移,发现枚举拐点,然后再枚举每个拐点那一层代表的高度即可,状态数只有 i(mi)(n+1i) 种,即枚举每列平台个数,再枚举平台高度。

熄灯了

然后预处理下记忆化搜索即可。

#include <bits/stdc++.h> #define ll long long #define pb push_back using namespace std; const int N=(int)(1e6+5); map<vector<int>,int>mp; vector<vector<int> >p; vector<int>vec[12],vec2[13],tmp,v; ll f[N]; bool vis[N]; int n,m,a[11][11],b[11][11],tot,ed; ll dfs(int x) { if(x==ed) { return 0ll; } if(vis[x]) return f[x]; bool fl=0; int res=0; for(int i:p[x]) { res+=i; } if(res&1) fl=1; else fl=0; for(int i=0;i<p[x].size();i++) { int qwq=p[x][i]; if(p[x][i]+1>n) continue ; bool ok=1; for(int j=0;j<i;j++) { if(p[x][j]<p[x][i]+1) ok=0; } if(ok) { v.clear(); for(int j=0;j<i;j++) v.pb(p[x][j]); v.pb(p[x][i]+1); for(int j=i+1;j<p[x].size();j++) v.pb(p[x][j]); int NEX=mp[v]; // cout<<x<<" "<<f[x]<<" "<<qwq<<" : "<<i<<" "<<fl<<" "; if(!fl) { f[x]=max(f[x],dfs(NEX)+a[qwq+1][i+1]); } else { // cout<<b[qwq+1][i+1]<<'\n'; f[x]=min(f[x],dfs(NEX)-b[qwq+1][i+1]); } } } // cout<<x<<" "<<f[x]<<'\n'; vis[x]=1; return f[x]; } signed main() { cin.tie(0); ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>a[i][j]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>b[i][j]; for(int S=0;S<(1<<m);S++) { if(S&1) vec[__builtin_popcount(S)].pb(S); } for(int S=0;S<(1<<(n+1));S++) { vec2[__builtin_popcount(S)].pb(S); } tot=-1; for(int i=1;i<=m;i++) { for(int S:vec[i]) { for(int T:vec2[i]) { v.clear(); tmp.clear(); for(int j=0;j<=n;j++) { if((T>>j)&1) tmp.pb(j); } int las=-1; for(int j=1;j<=m;j++) { if((S>>(j-1))&1) { v.pb(las=tmp.back()); tmp.pop_back(); } else { v.pb(las); } } // if(v.empty()) { // cout<<S<<' '<<T<<'\n'; // } mp[v]=++tot; p.pb(v); bool fl=1; int res=0; for(int x:v) { res+=x; if(x!=n) fl=0; } if(res&1) f[tot]=(ll)(2e16); else f[tot]=-(ll)(2e16); if(fl) { ed=tot; } // cout<<tot<<'\n'; // for(int x:v) cout<<x<<" "; // cout<<'\n'; } } } cout<<dfs(0); return 0; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/16667478.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示