题目链接:
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3761
题目意思:
二维矩阵上有n个球,沿水平或竖直打球,当A球与B球碰撞时,A球停在B球的位置,B求以A球的运动方向继续运动,球出边界后就消失了。打第一个球时,该球沿运动方向必须至少一个球才能打。求桌子上剩下的最少的球的个数,输出任意一种打球方案。
解题思路:
贪心+dfs
如果A能打,打了过后等价于A去掉,其他球的位置不变
首先预处理出每个点在4个方向上的最近的点,dfs,只要四个方向上存在一个球,就从该球开始继续往下搜,直到四个方向不存在球为止,搜完后回溯时记录方案。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#define Maxn 2200
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;
struct Point
{
int x,y;
}pp[Maxn];
struct Inf
{
int x,y;
int dd;
}ans[Maxn];
vector<int>myv[Maxn][5];//记录i点4个方向上最近的球的序号
bool vis[Maxn];
int n,dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}},cnt;
bool hav[5];
void dfs(int cur,int dd)
{
for(int k=1;k<=4;k++)
{
for(int p=0;p<myv[cur][k].size();p++)
{
if(vis[myv[cur][k][p]])
continue;
vis[myv[cur][k][p]]=true;
dfs(myv[cur][k][p],k);
}
}
++cnt;
ans[cnt].x=pp[cur].x;
ans[cnt].y=pp[cur].y;
ans[cnt].dd=dd; //记录打球的方案 倒着输出
}
int main()
{
//printf("%d\n",INF);
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
{
scanf("%d%d",&pp[i].x,&pp[i].y);
for(int j=1;j<=4;j++)
myv[i][j].clear();
}
for(int i=1;i<=n;i++)
{
int Max[5];
int temp[5];
for(int i=1;i<=4;i++)
Max[i]=INF;
for(int j=1;j<=n;j++)
{
if(i==j)
continue;
if(pp[j].x==pp[i].x) //上1 右2 下3 左4
{
if(pp[j].y>pp[i].y&&pp[j].y<Max[1])
{
Max[1]=pp[j].y; //向上的最近的球
temp[1]=j;
}
if(pp[j].y<pp[i].y&&pp[j].y<Max[3])
{
Max[3]=pp[j].y; //向下的最近的球
temp[3]=j;
}
}
if(pp[j].y==pp[i].y)
{
if(pp[j].x>pp[i].x&&pp[j].x<Max[2])
{
Max[2]=pp[j].x; //向右的最近的求
temp[2]=j;
}
if(pp[j].x<pp[i].x&&pp[j].x<Max[4])
{
Max[4]=pp[j].x; //向左的最近的球
temp[4]=j;
}
}
}
for(int k=1;k<=4;k++)
{
if(Max[k]!=INF)
myv[i][k].push_back(temp[k]);
}
}
memset(vis,0,sizeof(vis));
int an=0;
cnt=0;
for(int i=1;i<=n;i++)
{
if(vis[i])
continue;
an++; //每一块剩下一个球
vis[i]=true;
for(int k=1;k<=4;k++)
{
for(int p=0;p<myv[i][k].size();p++)
{
if(!vis[myv[i][k][p]])
{
vis[myv[i][k][p]]=true;
dfs(myv[i][k][p],k);
}
}
}
}
printf("%d\n",an);
for(int i=1;i<=cnt;i++)
{
printf("(%d, %d) ",ans[i].x,ans[i].y);
switch(ans[i].dd)
{
case 1:printf("DOWN\n");break;
case 2:printf("LEFT\n");break;
case 3:printf("UP\n");break;
case 4:printf("RIGHT\n");break;
}
}
}
return 0;
}
/*
2
0 0
1 1
4
0 0
1 0
1 1
2 2
6
0 0
1 0
1 1
2 2
3 3
3 2
*/