NOIP 2015 Senior 3
来源:互联网 发布:unity3d插件map lab 编辑:程序博客网 时间:2024/05/29 13:41
先看这道题的题目概况:给的时间是2s,内存是1G(真大= =),所以往搜索方面去想,然后来分析一下搜索时怎么出牌。想想自己斗地主的时候,是不是能多出就多出啊?不对啊,有时候我们不一定会出一个炸弹,而是选择留下一张牌等着出顺子。如果不能出顺子呢?那我们就放心了,能一次多出几张就多出几张吧。因此发现:如果不出顺子,出牌次数是可以通过贪心获得最优解的。贪心策略就是能出炸弹就出炸弹,能出三张就出三张,能出一对就出一对,不要拆开了,同时带得越多越好。那么,搜索的对象就出来了。我们只需要搜该不该出顺子,该出怎样的顺子就好了。
可以这样设计dfs的框架:
void dfs(int discardTime){ 最优性剪枝 计算不出顺子的次数,更新最优解 出顺子,深搜,回朔}
这种思路还是很简单,关键就看怎么写,看参考代码吧(其实我是参考了别人的,但是看懂了后真的好简单)。
参考代码
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;inline int readIn(){ int a; scanf("%d", &a); return a;}int n;int ans;int card[15];int cnt[5];void dfs(int discard = 0){ if(discard > ans) return; //最优性剪枝 memset(cnt, 0, sizeof(cnt)); for(int i = 0; i <= 14; i++) cnt[card[i]]++; //使用这种方式计算不出顺子的情况好算得多 int noStraight = 0; while(cnt[4]) //出炸弹 { cnt[4]--; noStraight++; if(cnt[2] >= 2) cnt[2]-=2; //四带二对 else if(cnt[1] >= 2) cnt[1]-=2; //四带二张 } while(cnt[3]) //出三张 { cnt[3]--; noStraight++; if(cnt[2]) cnt[2]--; //三带二 else if(cnt[1]) cnt[1]--; //三带一 } if(card[0] && card[1] && cnt[1] >= 2) noStraight--; //Joker是分开存的,但是可以单独打一对出去,在这里校正一下 noStraight += cnt[1] + cnt[2]; //加上没打的对子和单张牌 ans = std::min(noStraight + discard,ans); //更新最优解 for(int i = 3; i <= 14; i++) //从3到A,单顺子 { int j = i; for(; card[j] && j <= 14; j++) { card[j]--; if(j - i + 1 >= 5) dfs(discard + 1); //如果多于5张向下搜索 } while(j > i) card[--j]++; //回朔 } for(int i = 3; i <= 14; i++) //双顺子 { int j = i; for(; card[j] >= 2 && j <= 14; j++) { card[j] -= 2; if(j - i + 1 >= 3) dfs(discard + 1); } while(j > i) card[--j] += 2; } for(int i = 3; i <= 14; i++) //三顺子 { int j = i; for(; card[j] >= 3 && j <= 14; j++) { card[j] -= 3; if(j - i + 1 >= 2) dfs(discard + 1); } while(j > i) card[--j] += 3; }}int main(){ int a = readIn(); n = readIn(); while (a--) { ans = n; memset(card, 0, sizeof(card)); for(int i = 1; i <= n; ++i) { int x=readIn(); int y=readIn(); if(!x) card[y - 1]++; //Joker分别放在0和1,因为在顺子中不能带Joker else if(x == 1) card[14]++; //A放在14的位置,因为顺子的顺序是JQKA,刚好连上 else card[x]++; //其它的正常存 } dfs(); printf("%d\n",ans); } return 0;}
所以这道题最关键的地方就是明确要枚举什么。不是什么都去枚举,而是只枚举顺子。一个良好的程序结构也很重要,往往能让人事半功倍。尽管写了这么久代码了,但事实上一些实用的结构还需要多积累才好。
阅读全文
0 0
- NOIP 2015 Senior 3
- NOIP 2015 Senior 2
- NOIP 2015 Senior 5
- NOIP 2015 Senior 4
- NOIP 2015 Senior 6
- NOIP 2009 Senior 3
- NOIP 2011 Senior 3
- NOIP 2012 Senior 3
- NOIP 2014 Senior 3
- NOIP 2013 Senior 3
- NOIP 2016 Senior 3
- NOIP 2003 Senior 3
- NOIP 2005 Senior 3
- NOIP 2017 Senior 3
- NOIP 2009 Senior 1
- NOIP 2009 Senior 4
- NOIP 2011 Senior 2
- NOIP 2011 Senior 4
- ZCMU—1948
- iOS如何完美简单实现UITableView索引的放大悬浮提示View显示
- 学习笔记-Coondinatorlayout.Behavior<View>
- 动态规划:HDU3496-Watch The Movie(二维费用的背包问题)
- 以CalculationBrain为例的MVC机制,.h/.m文件解析
- NOIP 2015 Senior 3
- JVM性能调优之生成堆的dump文件
- 启动报错(持续更新)
- 具体截图
- get和post的区别
- Mina2.0学习笔记
- c++,二维数组中的数值代表金币数,从左上角出发,只能向下或者向右移动,判断是否存在获取指定金币数值N的路径
- webpack-dev-server不能自动刷新问题
- 20万、50万、100万的算法工程师,到底有什么区别?