正方形划分
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 479 Accepted Submission(s): 262
Problem Description
一个边长为L的正方形可以分成 L*L个小正方形. 有N个石子放在 N个小正方形里,能否将它分成 N个 正方形,使得每个正方形里恰有一个石子且这N 个正方形恰好构成整个正方形 .
Input
输入数据首先包含一个整数T,表示测试实例的个数,然后是T组数据,每组第一行包含2个整数L,N,接下来有N行每行2个整数 r,c,表示第r行c列的小正方形里有一个石子 .1<L<=20;1<N<=L*L; 1<=r,c<=L.
Output
对于每个测试实例,如能将它分成 N个 正方形输出YES, 否则输出 NO
Sample Input
3 5 8 2 4 3 3 3 4 3 5 4 2 4 4 4 5 5 5 3 2 1 1 3 3 2 4 1 1 1 2 2 1 2 2
Sample Output
YES NO YES
题意:给定边长为l的正方形,在里面放n个点,求能不能把这个正方形变成n个小正方形,并且每个正方形包含1个刚才的点。
思路:用dfs来做。很经典
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
int map[25][25];
int num[25][25]; //num[i][j]来记录(1,1)到(i,j)中包含的点de个数
bool vis[25][25]; //表示有没有被覆盖
int l,n;
bool dfs()
{
bool flag=false;
int x,y;
for(int i=1;i<=l;i++) //找到第一个没有被覆盖的坐标,以此为这个正方形的左上角
{
if(flag==true)break;
for(int j=1;j<=l;j++)
{
if(!vis[i][j])
{
x=i;
y=j;
flag=true;
break;
}
}
}
if(flag==false)return true; //直到所有的坐标都被覆盖则返回true
int k=0; //k为边长减1
while(x+k<=l&&y+k<=l) //寻找合适边长的正方形
{
int x1=x+k;
int y1=y+k;
int cnt=num[x1][y1]-num[x1][y-1]-num[x-1][y1]+num[x-1][y-1]; //利用num数组算出这个小正方形中的点数
if(cnt>1)break;
else if(cnt==1) //点为1的时候
{
bool can=true;
for(int i=x;i<=x1;i++)
{
for(int j=y;j<=y1;j++)
{
if(vis[i][j]==true)
{
can=false;
break;
}
}
}
if(can==true) //而且里面所有坐标都没被别的小正方形覆盖,就用当前小正方形覆盖,接着深搜
{
for(int i=x;i<=x1;i++)
for(int j=y;j<=y1;j++)
vis[i][j]=true;
if(dfs())return true;
for(int i=x;i<=x1;i++)
for(int j=y;j<=y1;j++)
vis[i][j]=false;
}
}
k++;
}
return false; //对于有没有覆盖的坐标而坐标形成正方形里无法覆盖点的情况返回false
}
int main()
{
int t;
cin>>t;
while(t--)
{
memset(map,0,sizeof(map));
memset(num,0,sizeof(num));
memset(vis,false,sizeof(vis));
cin>>l>>n;
int x,y;
for(int i=0;i<n;i++)
{
cin>>x>>y;
map[x][y]=1;
}
for(int i=1;i<=l;i++)
for(int j=1;j<=l;j++)
num[i][j]=num[i-1][j]+num[i][j-1]-num[i-1][j-1]+map[i][j];
if(dfs())cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}