• 中秋假期乱做


    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 即可反证。

    那么,这样暴力还是会有 \(10^{10}\) 状态数。

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

    熄灯了

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

    #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;
    }
    
  • 相关阅读:
    解决Windows 10每次重启默认浏览器都被重置为IE的一个办法
    使用cookie登录百度网盘账号
    【C++ Primer | 14】重载运算
    Visual Studio Code 快捷键的使用
    【C++ Primer | 8】IO库
    Git push常见用法
    Git 基础
    Git连接GitHub仓库详解
    ceph关于rpm包构建的教程
    monitor综合
  • 原文地址:https://www.cnblogs.com/xugangfan/p/16667478.html
Copyright © 2020-2023  润新知