题目大意:输入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集合中(方便找到一个点开始操作)。
此处有两个需要注意:
- 十字链表法存储的图适合查找同行同列的点,但是如果采用数组存储,那么此题目将不适用
- 采用map存储还是数组存储,主要看存储点的稀疏度和取值范围。如果取值范围不是很大,那么便可以采用数组存储;如果取值范围够大但是点数量比较稀疏,就可以采用map来存储