Easy billiards

题目链接

题目大意:输入n,代表平面内有n个点,接下来输入每个点的x,y坐标,均为整数。在动量守恒的前提下,每次朝一个方向拨动一个点(此方向上不能没有球),最后一个点将消失,问最少能有几个点保留在平面上。

题意转换:其实就是每次删去一个点,给定一个方向,且这个方向上要有点

思路:可以倒着思考这个问题。想象一下,假如最后只能剩下一个点,那么采用正确的策略时,最后一步一定是剩下了一个点。那么我们倒过来整个过程,在最后的一个点基础上选定一个方向,增加在输入集合中的一个点,并一直进行下去,直到不能增加为止,这样就可以得到一个可以删成一个点的点集。那么如果还有其他的点,均使用这个方式即可。

struct Point
{
    int x, y;
    string dir;
} ipt;

map<int, set<Point> >r, c;
set<Point>st;
set<Point>::iterator it;

int operator< (Point a, Point b)
{
    if (a.x != b.x)
        return a.x < b.x;
    else
        return a.y < b.y;
}

int n;
vector<Point> ans;
string t;

//0:上   1:下  2:左   3:右
void dfs(Point pt, string dir)
{
    int x = pt.x, y = pt.y;
    st.erase(pt);
    r[x].erase(pt);
    c[y].erase(pt);

    if (!r[x].empty())
    {
        it = r[x].begin();
        dfs(*it, it->y < y ? "UP" : "DOWN");
    }
    if (!c[y].empty())
    {
        it = c[y].begin();
        dfs(*it, it->x < x ? "RIGHT" : "LEFT");
    }

    if (dir.size() > 0)
    {
        ans.push_back(Point{x, y, dir});
    }
}

int main()
{
//    freopen("in.txt", "r", stdin);
    while (~RI(n))
    {
        r.clear();
        c.clear();
        st.clear();
        ans.clear();

        REP(kase, n)
        {
            RII(ipt.x, ipt.y);
            r[ipt.x].insert(ipt);
            c[ipt.y].insert(ipt);
            st.insert(ipt);
        }
        int cnt = 0;
        while (!st.empty())
        {
            cnt++;
            dfs(*(st.begin()), "");
        }
        cout << cnt << endl;
        int len = ans.size();
        REP(i, len)
        {
            printf("(%d, %d) ", ans[i].x, ans[i].y);
            cout << ans[i].dir << endl;
        }
    }
    return 0;
}

总结一下编程问题:

解法中涉及到找出与当前点同行或者同列的点,采用十字链表法存储整个图,并且冗余了所有的点集在st集合中(方便找到一个点开始操作)。

此处有两个需要注意:

  1. 十字链表法存储的图适合查找同行同列的点,但是如果采用数组存储,那么此题目将不适用
  2. 采用map存储还是数组存储,主要看存储点的稀疏度和取值范围。如果取值范围不是很大,那么便可以采用数组存储;如果取值范围够大但是点数量比较稀疏,就可以采用map来存储

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值