[贪心+dfs] ZOJ 3761 Easy billiards

题目链接:

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


*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值