首页 > 其他分享 >Gym101252B-Kakuro题解

Gym101252B-Kakuro题解

时间:2023-03-01 15:46:27浏览次数:56  
标签:dn 10 Kakuro run 格子 int 题解 Gym101252B rt

题目传送门

题意:有一个矩阵,每个格子为黑色或白色。你需要向白色格子中填 \(1\sim 9\) 的整整睡。从某一个黑色格子的右侧往右连续的一段极长的白色格子被称为一个 run,往下连续一段同理。在每个 run 的开始位置会标记出该 run 的数字和。除此之外要求每个 run 内填的数互不相同。求一组合法解。\(n,m\le 6\)。

首先想到朴素的搜索,搜索每个格子填的数,在每个 run 的末尾进行一次 check。过程中维护 vis 和当前 run 填的数之和,进行可行性剪枝。这种做法显然无法通过此题。

首先考虑强化可行性剪枝。常规的可行性剪枝为,假设后面全填从 \(1\) 开始的连续数列也比目标大/后面全填从 \(9\) 开始的连续数列也比目标小,此时进行剪枝。但是这种是不够强的,因为没有考虑到当前 run 可能已经使用过一些元素,进行排除。所以由于数据非常小,可以预处理出 \(f[i][j][k]\) 表示当前能用的数字几何为 \(i\),还剩 \(j\) 个格子待填,剩下的 \(j\) 个格子能否填到 \(k\)。这是一个不复杂的状压 DP,不再赘述。

然后就是非常重要的搜索顺序问题。显然每次我们希望找限制最强的位置搜索,具体地,先找限制最强的 run(尽快处理完),再在这个 run 内找限制最强的格子。前者可以通过维护每个 run 的数字使用情况 vis 和剩余格子 runleft 来计算,后者可以通过计算每个格子所在的两个 run 的 vis 的或来比较。这样看似在每次递归时都多消耗了复杂度来找限制最强的位置,但实际上这对效率的优化是及其显著的。

By cxm1024 From flydutchman

#include<bits/stdc++.h>
using namespace std;
int n,m,cntrun=0,cntgrid=0;
int flag[10][10],dn[10][10],rt[10][10];
int run[60],lt[30],up[30],runleft[60];
pair<int,int> grid[30],lu[30];
int vis[60];
bool f[512][10][56],done[30];
int a[30];
void print() {
	int now=0;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			if(flag[i][j]) cout<<"_ ";
			else cout<<a[++now]<<" ";
		}
		cout<<endl;
	}
	exit(0);
}
void dfs(int now) {
	if(now>cntgrid) {
		for(int i=1;i<=cntrun;i++)
			if(runleft[i]) return;
		print();
	}
	for(int i=1;i<=cntrun;i++)
		if(f[vis[i]^511][runleft[i]][run[i]]==0)
			return;
	int minx=10,minrun;
	for(int i=1;i<=cntrun;i++)
		if(run[i]!=0&&runleft[i]<minx)
			minx=runleft[i],minrun=i;
	int minn=10,mini;
	for(int i=1;i<=cntgrid;i++)
		if(!done[i]&&(lu[i].first==minrun||lu[i].second==minrun)) {
			int forb=vis[lu[i].first]|vis[lu[i].second];
			int tmp=9-__builtin_popcount(forb);
			if(tmp<minn)
				minn=tmp,mini=i;
		}
	done[mini]=1;
	for(int i=1;i<=9;i++) {
		if(vis[lu[mini].first]&(1<<(i-1))) continue;
		if(vis[lu[mini].second]&(1<<(i-1))) continue;
		vis[lu[mini].first]+=(1<<(i-1));
		vis[lu[mini].second]+=(1<<(i-1));
		runleft[lu[mini].first]--,runleft[lu[mini].second]--;
		run[lu[mini].first]-=i,run[lu[mini].second]-=i;
		a[mini]=i;
		if(f[vis[lu[mini].first]^511][runleft[lu[mini].first]][run[lu[mini].first]])
			if(f[vis[lu[mini].second]^511][runleft[lu[mini].second]][run[lu[mini].second]])
				dfs(now+1);
		vis[lu[mini].first]^=(1<<(i-1));
		vis[lu[mini].second]^=(1<<(i-1));
		runleft[lu[mini].first]++,runleft[lu[mini].second]++;
		run[lu[mini].first]+=i,run[lu[mini].second]+=i;
		a[mini]=0;
	}
	done[mini]=0;
}
signed main() {
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	for(int s=0;s<512;s++) {
		f[s][0][0]=1;
		for(int i=1;i<=9;i++) {
			if((s&(1<<(i-1)))==0) continue;
			for(int j=9;j>=0;j--)
				for(int k=0;k<=55;k++)
					if(f[s][j][k])
						f[s][j+1][k+i]=1;
		}
	}
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) {
			string s;
			cin>>s;
			if(s[2]=='.') {
				flag[i][j]=0;
				grid[++cntgrid]={i,j};
				rt[i][j]=rt[i][j-1];
				dn[i][j]=dn[i-1][j];
				lu[cntgrid]={dn[i][j],rt[i][j]};
				runleft[rt[i][j]]++;
				runleft[dn[i][j]]++;
			}
			else if(s[2]=='X')
				flag[i][j]=-1;
			else {
				flag[i][j]=1;
				if(s[0]!='X')
					run[dn[i][j]=++cntrun]=(s[0]-'0')*10+s[1]-'0';
				if(s[3]!='X')
					run[rt[i][j]=++cntrun]=(s[3]-'0')*10+s[4]-'0';
			}
		}
	dfs(1);
	return 0;
}

标签:dn,10,Kakuro,run,格子,int,题解,Gym101252B,rt
From: https://www.cnblogs.com/cxm1024/p/17168452.html

相关文章

  • C#、IIS获取时间带星期或日期问题解决
    ,获取时间老是带星期,如:2018-8-8星期日12:00:00,造成写入数据库时无法识别为时间格式报错。试了修改区域语言和修改指定注册表均无效。   解决方法一:在代码里处理时......
  • SPOJ Query On A Tree IV 题解
    SPOJQueryOnATreeIV题解一个边分治套线段树套堆的题目比较难写但是有不小的启发思路来源和代码都抄自[SPOJ-QTREE4]QUERYONATREEIV题解|KSKUN'sBlog简......
  • [CF282D] Yet Another Number Game 题解
    [CF282D]YetAnotherNumberGame传送门这题可以分三种情况讨论\(n\)的取值。n=1显然,当\(a1\neq0\)时先手可以一下全部取完,后手必败,否则后手必胜。n=2有......
  • AtCoder Beginner Contest 275 A-F 题解
    比赛链接砳赫賏,看懂扣1(A-FindTakahashi模拟。#include<cstdio>#include<algorithm>usingnamespacestd;intn,mx,id=1;intmain(){ intx; scanf("%d......
  • Codeforces Round #854 by cybercats (Div. 1 + Div. 2) A-B题解
    比赛链接A可以发现,每次出去的顺序一定是按照\(n->1\)的顺序。因为新加入的东西只会放在最前面。相应的,如果其已在序列中,则这个操作不会对\(1~n\)的信息产生影响。......
  • border出现虚边问题解决
    当我们只给元素设置了border-top,没有设置其他边框的时候,如果我们使用了border-radius会出现虚边的情况,如下所示:css代码:div{width:100px;height:100px;border-top:2pxsoli......
  • 跨域问题解决
    目录跨域请求问题解决解决跨域问题方式简单请求与非简单请求自己编写中间件处理跨域请求问题解决同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全......
  • Codeforces Round #854 by cybercats (Div. 1+2) 1799 A~G 题解
    点我看题A.RecentActions注意到只有编号大于n的博客会被更新,所以每当有一个之前没被更新的过的博客被更新时,当前列表中最下面的就会被挤掉。如果这次更新的博客之前已......
  • [洛谷]P5401 [CTS2019] 珍珠 题解
    [洛谷]P5401[CTS2019]珍珠题解题意概述有\(D\)种珍珠,每种有无限颗,现在等概率的从\(D\)种珍珠中抽\(n\)次珍珠,每次抽\(1\)个珍珠,记第\(i\)种珍珠最后一共抽......
  • 江南信息学2023年第一周练习20230223 题解
    比赛链接1001:鸡尾酒疗法1#include<bits/stdc++.h>2usingnamespacestd;3intmain()4{5intn;6cin>>n;7doublea,b;8cin>>a......