OJ 7223 至少有多少只恼人的大青蛙?__深搜
来源:互联网 发布:域名注册 不需要备案 编辑:程序博客网 时间:2024/05/17 08:16
描述
有一种大青蛙会跳越稻田,从而踩踏稻子。农民在早上看到被踩踏的稻子,希望知道晚上有多少青蛙穿越自家的稻田。每只青蛙总是沿着一条直线跳跃稻田,而且每次跳跃的距离都相同。
如图1和图2所示,稻田里的稻子组成一个栅格,每棵稻子位于一个格点上。而青蛙总是从稻田的一侧跳进稻田,然后沿着某条直线穿越稻田,从另一侧跳出去。
如图3所示,可能会有多只青蛙从稻田穿越。青蛙的每一跳都恰好踩在一棵水稻上,将这棵水稻踩坏。有些水稻可能被多只青蛙踩踏。当然,农民所见到的是图4中的情形,并看不到图3中的直线,也见不到别人家田里被踩踏的水稻。需要注意的是,农民可以根据水稻踩坏的程度来精确计算出一共几只青蛙踩踏过这一棵水稻,所以聪明的农民就能得到图4的水稻毁坏程度图。
我们可以认为每条青蛙跳跃路径上至少包括3棵被踩踏的水稻。而在一条青蛙跳跃路径的直线上,也可能会有些被踩踏的水稻不属于该行走路径,例如青蛙跳跃路径(2,3)(4,5)(6,7)经过(3,4),而(3,4)不属于跳跃路径。
注意:同一只青蛙不会第二次跳入同一片稻田,可能存在不同的两只青蛙的跳跃路径完全一样。
现在给定水稻破坏程度图,请你写一个程序,确定:最少有多少只青蛙穿越了稻田。例如,图4的答案是4,图3所示的跳跃路径数量就是答案。输入数据保证有解且最多14只青蛙穿越稻田。
输入
从标准输入设备上读入数据。第一行为测试数据数目。每组测试数据第一行上两个整数R、C,分别表示稻田中水稻的行数和列数,1≤R、C≤50。第二行是一个整数N,表示被踩踏的水稻数量, 3≤N≤700。在剩下的N行中,每行有三个整数,分别是一颗被踩踏水稻的行号(1~R)、列号(1~C)和被踩踏次数,三个整数之间用空格隔开。每棵被踩踏水稻只被列出
输出
对于每一组测试数据从标准输出设备上输出一个整数,表示最少有几只青蛙穿越了稻田。
样例输入
1
6 7
14
2 1 1
2 2 1
2 3 2
2 4 1
2 5 1
2 6 2
2 7 1
3 4 1
4 2 1
4 5 1
6 1 1
6 3 1
6 5 1
6 7 2
样例输出
4
分析
最开始看的题就是恼人的青蛙,最后一道题没想到会又与这个话题有关。枚举前两个点,之后再递归看能否让所有的点归于有效路径里面,按原来的思路改写后TLE,之后又加了些剪枝方式,示例降到1ms,但OJ上测试还是TLE,尝试多次无果,先放着吧,有空再想想,如果有了解怎么有效剪枝麻烦回复下。
实现
#include <iostream>#include <algorithm>#include <vector>#include <ctime>#include <map>#define KEY(plant) ((plant.x << 6) + plant.y)using namespace std;int r, c, n, totalPlantCount, minPathCount;struct PLANT{ int x, y, num;};PLANT plants[701];PLANT plant;map<int, int> pointMap;vector<int> searchPath(int i, int j, PLANT secPlant, int dX, int dY);void findNextPathStartPlant(int startX, int plantCount, int pathCount) { //如果所有践踏点都已覆盖掉的话则更新最小路径数,即最小青蛙数。 if (plantCount == 0) { minPathCount = minPathCount > pathCount ? pathCount : minPathCount; return; } if (pathCount >= minPathCount) { return; } int dX, dY, pX, pY, findFirstJ; for (int i = startX; i < n - 2; i++) { if (plants[i].num <= 0) continue;//plants[i]是第一个点,必须保证未用完踩踏点。 findFirstJ = 0; for (int j = i + 1; j < n - 1; j++) { if (plants[j].num <= 0) continue;//plants[j]是第二个点,必须保证未用完踩踏点。 if (plants[i].num <= 0) break; if (!findFirstJ) { findFirstJ = j; } dX = plants[j].x - plants[i].x; dY = plants[j].y - plants[i].y; pX = plants[i].x - dX; pY = plants[i].y - dY; //第一点的前一点在稻田里, 说明本次选的第二点导致的x方向步长不合理(太小) //取下一个点作为第二点 if (pX <= r && pX >= 1 && pY <= c && pY >= 1) continue; //x方向过早越界了. 说明本次选的第二点不成立 //如果换下一个点作为第二点, x方向步长只会更大, 更不成立, 所以应该 //认为本次选的第一点必然是不成立的, 那么取下一个点作为第一点再试 pX = plants[j].x + dX; if (pX > r || pX < 1) break; pY = plants[j].y + dY; if (pY > c || pY < 1) continue; //y方向过早越界了, 应换一个点作为第二点再试 vector<int> plantPos = searchPath(i, j, plants[j], dX, dY); if (plantPos.size() > 0) { plantCount -= plantPos.size(); //是一条路径则将所有踩踏点的可踩踏数减1。 for (std::vector<int>::const_iterator iter = plantPos.cbegin(); iter != plantPos.cend(); ++iter) {// PLANT currPlant = plants[(*iter)]; // cout << "(" << currPlant.x << ", " << currPlant.y << ", " << currPlant.num << ") "; plants[(*iter)].num--; } findNextPathStartPlant(i + 1, plantCount, pathCount + 1); //恢复 plantCount += plantPos.size(); for (std::vector<int>::const_iterator iter = plantPos.cbegin(); iter != plantPos.cend(); ++iter) { plants[(*iter)].num++; } } } if (plants[i].num > 0) { //当前点没消耗完说明是孤立点,永远不可能踩到。 return; } if (findFirstJ) { i = findFirstJ; } //直到跳到下一个可用的点。 }}int main() {// freopen("in.txt", "r", stdin);// const clock_t begin_time = std::clock(); int t; scanf("%d", &t); while (t--) { totalPlantCount = 0; minPathCount = 15; pointMap.clear(); scanf("%d %d", &r, &c); //行数和列数, x方向是上下, y方向是左右 scanf("%d", &n); for (int i = 0; i < n; i++) { scanf("%d %d %d", &plants[i].x, &plants[i].y, &plants[i].num); pointMap[KEY(plants[i])] = i; totalPlantCount += plants[i].num; } //将水稻按x坐标从小到大排序, x坐标相同按y从小到大排序 sort(plants, plants + n); findNextPathStartPlant(0, totalPlantCount, 0); printf("%d\n", minPathCount); }// std::cout << float(clock() - begin_time) << "ms" << endl;}bool operator <(const PLANT& p1, const PLANT& p2) { if (p1.x == p2.x) return p1.y < p2.y; return p1.x < p2.x;}bool operator ==(const PLANT& p1, const PLANT& p2) { return p1.x == p2.x && p1.y == p2.y;}int binarySearch(PLANT* a, int start, int end, PLANT key) { int l = start, r = end - 1; while (l <= r) { int mid = l + (r - l) / 2; if (a[mid] == key) return mid; else if (a[mid] < key) l = mid + 1; else r = mid - 1; } return -1;}//判断从 secPlant点开始, 步长为dx, dy, 那么最多能走几步vector<int> searchPath(int i, int j, PLANT secPlant, int dX, int dY) { vector<int> plantPos; plantPos.push_back(i); plantPos.push_back(j); PLANT plant; plant.x = secPlant.x + dX; plant.y = secPlant.y + dY;// int pos = j, end; while (plant.x <= r && plant.x >= 1 && plant.y <= c && plant.y >= 1) {// end = pos + r * dX + dY + 1;// if (end >= n) {// end = n;// }// pos = binarySearch(plants, pos, end, plant); int key = KEY(plant); if (pointMap.count(key) > 0 && plants[pointMap[key]].num > 0) { plantPos.push_back(pointMap[key]); plant.x += dX; plant.y += dY; } else { //not found, 每一步都必须踩倒水稻才算合理, 否则这就不是一条行走路径 plantPos.clear(); break; } } return plantPos;}
- OJ 7223 至少有多少只恼人的大青蛙?__深搜
- 期末考试-至少有多少只恼人的大青蛙?(算法基础 第10周)
- 程序设计实习上机练习43 至少有多少只恼人的大青蛙?
- 【正解】Openjudge 至少有多少只恼人的大青蛙 (dfs 剪枝 好题)
- 程序设计实习上机练习43 至少有多少只恼人的大青蛙?(搜索+贪心,至今未解决)
- 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
- 一只青蛙一次可以跳上 1 级台阶,也可以跳上2 级。求该青蛙跳上一个n 级的台阶总共有多少种跳法
- java 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
- 一只青蛙一次可以跳上1—n级台阶。求该青蛙跳上一个n级的台阶总共有多少种跳法?(递归)
- 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
- 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
- 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
- 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
- 一只青蛙一次可以跳上1级台阶也可以跳上2级求该青蛙跳上一个n级的台阶总共有多少种跳法
- POJ 1054 恼人的青蛙 [ 枚举+剪枝 ]
- NOI题库1814 恼人的青蛙
- 有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡至少要多少只小白鼠才能在24小时
- 他们三人都答对的题目至少有多少?
- class用法
- HDU4252:A Famous City
- 今天开通CSDN博客
- 2016—2017年寒假总结
- LeetCode 63 --- Unique Paths II
- OJ 7223 至少有多少只恼人的大青蛙?__深搜
- C++阶段性学习总结重难点
- 动态规划题集
- CSS属性之列表属性
- MFC下读取TXT点云文件
- Tomcat配置虚拟目录
- apache性能测试工具ab使用详解
- spring原理
- 方差分析