首页 > 其他分享 >Gym100753D-Carpets题解

Gym100753D-Carpets题解

时间:2023-03-01 15:47:34浏览次数:60  
标签:nxt cnt int 题解 ++ lst Gym100753D now Carpets

题目传送门

题意:有一个 \(H\times W\) 的地板和 \(n\) 个矩形地毯,问是否能不重不漏地填满地板。\(H,W\le 100,n\le 7\)。

考虑朴素的搜索,每次考虑最左上角的没填的位置,枚举用哪块地毯覆盖(因为这个格子肯定需要被覆盖,而不可能被更左上的格子覆盖)。先检查是否可以用这块地毯,如果可以用则把这块地毯填上,继续搜索,回溯时撤销操作。

这样虽然可以通过本题,但速度比较慢,考虑剪枝。思考可以发现,大多数情况下面积根本无法凑到恰好 \(H\times W\),所以可以提前枚举用哪些地毯,如果这个地毯集合能凑到恰好 \(H\times W\)
的面积,就强行钦定只能用这些地毯再搜索。代码如下:

By cxm1024 From lqs2015

#include<bits/stdc++.h>
using namespace std;
int n,m,q;
struct node{int x,y;} a[10];
bool vis[10],cov[110][110];
void dfs() {
	int x=-1,y=-1;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++)
			if(!cov[i][j]) {
				x=i,y=j;
				break;
			}
		if(x!=-1) break;
	}
	if(x==-1) {
		cout<<"yes"<<endl;
		exit(0);
	}
	for(int j=1;j<=q;j++) {
		if(vis[j]) continue;
		vis[j]=1;
		for(int t=0;t<=1;t++,swap(a[j].x,a[j].y)) {
			bool flag=1;
			for(int xx=x;xx<x+a[j].x;xx++)
				for(int yy=y;yy<y+a[j].y;yy++)
					if(xx>n||yy>m||cov[xx][yy]) flag=0;
			if(flag) {
				for(int xx=x;xx<x+a[j].x;xx++)
					for(int yy=y;yy<y+a[j].y;yy++)
						cov[xx][yy]=1;
				dfs();
				for(int xx=x;xx<x+a[j].x;xx++)
					for(int yy=y;yy<y+a[j].y;yy++)
						cov[xx][yy]=0;
			}
		}
		vis[j]=0;
	}
}
signed main() {
	cin>>n>>m>>q;
	int tot=0;
	for(int i=1;i<=q;i++) {
		int num,x,y;
		cin>>num>>x>>y;
		while(num--) a[++tot]={x,y};
	}
	q=tot;
	for(int s=1;s<(1<<q);s++) {
		int sum=0;
		for(int i=1;i<=q;i++)
			if(s&(1<<(i-1))) sum+=a[i].x*a[i].y;
			else vis[i]=1;
		if(sum==n*m) dfs();
		for(int i=1;i<=q;i++) vis[i]=0;
	}
	cout<<"no"<<endl;
	return 0;
}

加了这个优化后可以达到 100ms 以内,但离最优解还有一定距离。接下来我加的优化为改变搜索的内容。

我们在搜索的过程中钦定从左上角开始填,记录当前的轮廓线(可以用链表维护)。每次枚举每个拐角来添加地毯。添加的时候要求添加后仍然为从右上到左下的一条折线。这一步的正确性是有保证的,因为最终的解一定能通过这个限制搜到,原因是,如果最终解中每个拐角填的地毯都不为一条折线,那么考虑最右上的拐角,它显然不能往右超出这个拐角的范围,只能往下。而右上的第二个拐角由于右边被第一个拐角超出挡住,所以只能往下。以此类推到最后一个拐角也只能往下,而这显然是不可能的。所以最终解一定能通过这个限制被搜到。

用这个搜索方式加上面的剪枝就拿到了 15ms 的最优解。

By cxm1024

#include<bits/stdc++.h>
using namespace std;
int n,m,q;
struct node{int x,y;} a[10];
bool vis[10];
pair<int,int> now[20];
int head,tail,lst[20],nxt[20],cnt=0;
void dfs() {
	if(nxt[head]==tail) {
		cout<<"yes"<<endl;
		exit(0);
	}
	for(int i=nxt[head];i!=tail;i=nxt[i]) {
		int fir=now[i].first,sec=now[i].second;
		for(int j=1;j<=q;j++) {
			if(vis[j]) continue;
			for(int t=0;t<=1;t++,swap(a[j].x,a[j].y)) {
				if(a[j].x>fir||a[j].y>sec) continue;
				vis[j]=1;
				if(a[j].x==fir&&a[j].y==sec) {
					now[nxt[i]].first+=fir,now[lst[i]].second+=sec;
					nxt[lst[i]]=nxt[i],lst[nxt[i]]=lst[i];
					dfs();
					now[nxt[i]].first-=fir,now[lst[i]].second-=sec;
					nxt[lst[i]]=i,lst[nxt[i]]=i;
				}
				else if(a[j].x==fir) {
					now[i].second-=a[j].y,now[lst[i]].second+=a[j].y;
					dfs();
					now[i].second+=a[j].y,now[lst[i]].second-=a[j].y;
				}
				else if(a[j].y==sec) {
					now[i].first-=a[j].x,now[nxt[i]].first+=a[j].x;
					dfs();
					now[i].first+=a[j].x,now[nxt[i]].first-=a[j].x;
				}
				else {
					now[++cnt]={fir-a[j].x,a[j].y};
					nxt[lst[i]]=cnt,lst[cnt]=lst[i];
					now[++cnt]={a[j].x,sec-a[j].y};
					lst[cnt]=cnt-1,nxt[cnt-1]=cnt;
					nxt[cnt]=nxt[i],lst[nxt[i]]=cnt;
					dfs();
					lst[nxt[i]]=i,nxt[lst[i]]=i,cnt-=2;
				}
				vis[j]=0;
			}
		}
	}
}
signed main() {
	cin>>n>>m>>q;
	int tot=0;
	for(int i=1;i<=q;i++) {
		int num,x,y;
		cin>>num>>x>>y;
		while(num--) a[++tot]={x,y};
	}
	q=tot;
	head=++cnt,tail=++cnt;
	now[++cnt]={m,n},lst[cnt]=head,nxt[cnt]=tail;
	nxt[head]=cnt,lst[tail]=cnt;
	for(int s=1;s<(1<<q);s++) {
		int sum=0;
		for(int i=1;i<=q;i++)
			if(s&(1<<(i-1))) sum+=a[i].x*a[i].y;
			else vis[i]=1;
		if(sum==n*m) dfs();
		for(int i=1;i<=q;i++) vis[i]=0;
	}
	cout<<"no"<<endl;
	return 0;
}

标签:nxt,cnt,int,题解,++,lst,Gym100753D,now,Carpets
From: https://www.cnblogs.com/cxm1024/p/17168448.html

相关文章

  • Gym100825C-KenKen-You-Do-It题解
    题目传送门题意:在一个矩阵上选中了若干个格子,保证连通。你需要在这些格子中填数,使得:每行每列不能重复,且这些数进行给定运算(可以认为只有加法和乘法)的结果为给定的数值。......
  • Gym100959I-Robots题解
    题意:平面直角坐标系上有\(n\)个机器人,每个机器人有一个上下左右之一的方向,初始所有机器人静止。在第\(0\)秒,你让第一个机器人开始朝它的方向移动,速度为每秒一个单位。......
  • Gym101252B-Kakuro题解
    题目传送门题意:有一个矩阵,每个格子为黑色或白色。你需要向白色格子中填\(1\sim9\)的整整睡。从某一个黑色格子的右侧往右连续的一段极长的白色格子被称为一个run,往下......
  • 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)是一种约定,它是浏览器最核心也最基本的安全......