POJ - 1084___Square Destroyer —— IDA* | DLX重复覆盖

2023-11-02 10:58

本文主要是介绍POJ - 1084___Square Destroyer —— IDA* | DLX重复覆盖,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接:点我啊╭(╯^╰)╮

题目大意:

     给你 n ∗ n n*n nn的由火柴组成的正方形图案 ( n ≤ 5 ) (n≤5) n5, 并对每个火柴进行编号,已经帮你删除了 k k k 个火柴棒,请问最少还要删除几根火柴棒,使得由火柴组成的图形没有一个完整的正方形,正方形的边长可以为 1 、 2...... 1、2...... 12......

解题思路:

    首先我们先算出,一共有 2 ∗ n ∗ ( n + 1 ) 2*n*(n+1) 2n(n+1)根火柴, 1 2 + 2 2 + … + n 2 = n ( n + 1 ) ( 2 n + 1 ) / 6 1^2+2^2+…+n^2=n(n+1)(2n+1)/6 12+22++n2=n(n+1)(2n+1)/6个正方形,那么每个正方形只要有一个火柴缺失,这个正方形就不完整,继而转化为了DLX重复覆盖模型
    每个火柴为每一行,每个正方形为每列,要求使用最少的火柴重复覆盖每个正方形,明显套个模板就行
    只看上面的思路的话你可能会认为这道题是道水题,但发现一实现起来并不是那么简单 . . . . . . ...... ......,因为本题最难的地方并不是让你想到DLX,而是怎么去建图(个人感觉)
    要注意的是他已经帮你除掉一些火柴了,那么这些火柴所涉及到的正方形永远都不完整,所以我们一开始并不着急去建图,先将需要连的点用 m a r k mark mark记录下来,然后去看他已经删除了哪些火柴,然后对这些火柴涉及到的列 j j j 进行以下操作:

dlx.L[dlx.R[j]] = dlx.L[j];
dlx.R[dlx.L[j]] = dlx.R[j];
dlx.R[j] = dlx.L[j] = 0;

    这样这列就永远删除了,最后就将 m a r k mark mark记录的需要连的边连上就好,然而这并没有我我想像的那么简单,具体看下

代码思路:

    怎么处理 2 ∗ n ∗ ( n + 1 ) 2*n*(n+1) 2n(n+1)根火柴与 n ( n + 1 ) ( 2 n + 1 ) / 6 n(n+1)(2n+1)/6 n(n+1)(2n+1)/6个正方形之间的边呢???
    这里我是依次处理边长为 1 、 2...... n 1、2......n 12......n的正方形,设正方形的边长为 s i si si,其每行就有 n − s i + 1 n-si+1 nsi+1根火柴,每列也有 n − s i + 1 n-si+1 nsi+1根火柴,同时每条边也会有 s i si si 根火柴
    观察每个正方形的每条边的火柴数量规律(先算边长为 1 1 1 的),发现每个正方形的上边和下边满足同一个规律,左边和右边满足一个规律,用这两个规律即可建图,具体代码如下:

	int c=1;for(int si=1; si<=n; si++) {for(int i=1; i<=n-si+1; i++) {for(int j=1; j<=n-si+1; j++) {for(int k=0; k<si; k++) {mark[(i-1)*(2*n+1)+j+k][c]=true;	// 上 mark[(i-1+si)*(2*n+1)+j+k][c]=true;	// 下 mark[i*n+(i-1)*(n+1)+j+k*(2*n+1)][c]=true;	// 左 mark[i*n+(i-1)*(n+1)+j+k*(2*n+1)+si][c]=true;	// 右 }c++;}}}

    最后一个点!!!
    在前面我们对已经删除火柴的边所涉及到的正方形(即DLX中的列)进行删除时,记录下来这些列,在后来的加边过程中不要加这些边(这很重要,不然你会错)

核心:熟悉的使用覆盖这一技巧,同时对建图有熟练地操作

本题在没注意数据范围而调了很长时间,我真是个傻B。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;#define N 50010
#define M 1010
struct DancingLink {int n,s,ansd;//列数 节点总数int S[M],A[M],H[M];//S[]该列节点总数  A[]答案  H[]行首指针int L[N],R[N],U[N],D[N]; //L[],R[],U[],D[] 上下左右int X[M],C[M],vis[M];//X[] C[] 行列编号void init(int n) { //初始化this->n=n;for(int i=0; i<=n; i++)U[i]=i,D[i]=i,L[i]=i-1,R[i]=i+1;R[n]=0,L[0]=n;s=n+1;memset(S,0,sizeof(S));memset(H,-1,sizeof(H));}void DelCol(int c) { //删除列for(int i=D[c]; i!=c; i=D[i])L[R[i]]=L[i],R[L[i]]=R[i];}void ResCol(int c) { //恢复列for(int i=U[c]; i!=c; i=U[i])L[R[i]]=R[L[i]]=i;}void AddNode(int r,int c) { //添加节点++S[c],C[++s]=c,X[s]=r;D[s]=D[c],U[D[c]]=s,U[s]=c,D[c]=s;if(H[r]<0) H[r]=L[s]=R[s]=s;//行首节点else  R[s]=R[H[r]],L[R[H[r]]]=s,L[s]=H[r],R[H[r]]=s;}int f() {int ret=0;memset(vis,0,sizeof(vis));for(int i=R[0]; i; i=R[i])if(!vis[i]) {ret++;vis[i]=1;for(int j=D[i]; j!=i; j=D[j])for(int k=R[j]; k!=j; k=R[k])vis[C[k]]=1;}return ret;}void dfs(int d) { //深度,深搜遍历if(!R[0]) {ansd=min(d,ansd);return;}if(d+f()>=ansd) return;int c=R[0];for(int i=R[0]; i; i=R[i]) if(S[i]<S[c]) c=i;for(int i=D[c]; i!=c; i=D[i]) {DelCol(i);for(int j=R[i]; j!=i; j=R[j]) DelCol(j);dfs(d+1);for(int j=L[i]; j!=i; j=L[j]) ResCol(j);ResCol(i);}}} dlx;int main() {bool visx[80];bool mark[80][80];int n,m,cas,q;scanf("%d", &cas);while(cas--) {scanf("%d%d",&n,&q);int row=2*n*(n+1), col=0;for(int i=1; i<=n; i++) col += i*i;dlx.init(col);memset(mark,0,sizeof(mark));memset(visx,0,sizeof(visx));int c=1,tmp;for(int si=1; si<=n; si++) {for(int i=1; i<=n-si+1; i++) {for(int j=1; j<=n-si+1; j++) {for(int k=0; k<si; k++) {mark[(i-1)*(2*n+1)+j+k][c]=true;	// 上 mark[(i-1+si)*(2*n+1)+j+k][c]=true;	// 下 mark[i*n+(i-1)*(n+1)+j+k*(2*n+1)][c]=true;	// 左 mark[i*n+(i-1)*(n+1)+j+k*(2*n+1)+si][c]=true;	// 右 }c++;}}}for(int i=0; i<q; i++){scanf("%d", &tmp);for(int j=1; j<=col; j++){if(mark[tmp][j] && !visx[j]){visx[j] = 1;dlx.L[dlx.R[j]] = dlx.L[j];dlx.R[dlx.L[j]] = dlx.R[j];dlx.R[j] = dlx.L[j] = 0;}}}for(int i=1; i<=row; i++)for(int j=1; j<=col; j++)if(mark[i][j] && !visx[j])dlx.AddNode(i,j);dlx.ansd = 100000;dlx.dfs(0);printf("%d\n", dlx.ansd);}
}

这篇关于POJ - 1084___Square Destroyer —— IDA* | DLX重复覆盖的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/330128

相关文章

如何通过try-catch判断数据库唯一键字段是否重复

《如何通过try-catch判断数据库唯一键字段是否重复》在MyBatis+MySQL中,通过try-catch捕获唯一约束异常可避免重复数据查询,优点是减少数据库交互、提升并发安全,缺点是异常处理开... 目录1、原理2、怎么理解“异常走的是数据库错误路径,开销比普通逻辑分支稍高”?1. 普通逻辑分支 v

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

nginx 负载均衡配置及如何解决重复登录问题

《nginx负载均衡配置及如何解决重复登录问题》文章详解Nginx源码安装与Docker部署,介绍四层/七层代理区别及负载均衡策略,通过ip_hash解决重复登录问题,对nginx负载均衡配置及如何... 目录一:源码安装:1.配置编译参数2.编译3.编译安装 二,四层代理和七层代理区别1.二者混合使用举例

MySQL中查找重复值的实现

《MySQL中查找重复值的实现》查找重复值是一项常见需求,比如在数据清理、数据分析、数据质量检查等场景下,我们常常需要找出表中某列或多列的重复值,具有一定的参考价值,感兴趣的可以了解一下... 目录技术背景实现步骤方法一:使用GROUP BY和HAVING子句方法二:仅返回重复值方法三:返回完整记录方法四:

XML重复查询一条Sql语句的解决方法

《XML重复查询一条Sql语句的解决方法》文章分析了XML重复查询与日志失效问题,指出因DTO缺少@Data注解导致日志无法格式化、空指针风险及参数穿透,进而引发性能灾难,解决方案为在Controll... 目录一、核心问题:从SQL重复执行到日志失效二、根因剖析:DTO断裂引发的级联故障三、解决方案:修复

SpringBoot+Redis防止接口重复提交问题

《SpringBoot+Redis防止接口重复提交问题》:本文主要介绍SpringBoot+Redis防止接口重复提交问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录前言实现思路代码示例测试总结前言在项目的使用使用过程中,经常会出现某些操作在短时间内频繁提交。例

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

使用C#删除Excel表格中的重复行数据的代码详解

《使用C#删除Excel表格中的重复行数据的代码详解》重复行是指在Excel表格中完全相同的多行数据,删除这些重复行至关重要,因为它们不仅会干扰数据分析,还可能导致错误的决策和结论,所以本文给大家介绍... 目录简介使用工具C# 删除Excel工作表中的重复行语法工作原理实现代码C# 删除指定Excel单元

Java如何用乘号来重复字符串的功能

《Java如何用乘号来重复字符串的功能》:本文主要介绍Java使用乘号来重复字符串的功能,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java乘号来重复字符串的功能1、利用循环2、使用StringBuilder3、采用 Java 11 引入的String.rep

SQL常用操作精华之复制表、跨库查询、删除重复数据

《SQL常用操作精华之复制表、跨库查询、删除重复数据》:本文主要介绍SQL常用操作精华之复制表、跨库查询、删除重复数据,这些SQL操作涵盖了数据库开发中最常用的技术点,包括表操作、数据查询、数据管... 目录SQL常用操作精华总结表结构与数据操作高级查询技巧SQL常用操作精华总结表结构与数据操作复制表结