HDU 5031:看脸的DFS
2014-09-22 11:20
309 查看
我仍然觉得这道题十分的不科学……
题目是说你有一个坐标系中的点阵,其中点的两个坐标范围分别为0~n和0~m,而且都是整数点。你的好基友在这坐标系上画了几条直线,每条直线保证只穿过整数点,而且至少穿过3个整数点。现在你得到了一个矩阵,矩阵描述了每个点都被穿过了多少次,你需要确定你的基友最少画了多少条直线。答案保证不超过14.
这真没什么好办法,只能搜索,但是如果真老老实实地搜,肯定会T到死……
既然要搜索,我们肯定要找到这里所有的直线,不妨先看看怎样不重不漏地找出这里所有的直线吧,我的算法是这样的:先预处理出所有不是0的点,从左向右,从上向下扫描每个点,以它为起点,然后从它开始继续从左向右,从上到下枚举每个点作为终点,如果这两个点确定的向量的两个分量不互质(例如(1,1)和(3,3)向量是(2,2)),那么肯定没戏,他们中间一定隔过了一个点,或者中间的点是0;设这个向量为vec;
然后从起点开始每次减去vec枚举,看到0就说明没戏,看到不为0的点也说明没戏,因为我是从左向右,从上到下扫描的,假如我看到一个在我上面的(或左边的)点,那么应该是这个点来找我才对,这说明这条直线已经被找过了,所以不要;
接下来从终点每次加上vec枚举,看到0说明没戏,而且要注意记录一下是否看到了至少一个1,如果是的话就ok,这就是一条满足要求的直线了。
然后说搜索,我最后AC时采用的搜索姿势是:
(1)找线的工作放在每层搜索之前做,这很重要,因为你会发现找到一条直线并划去这条直线之后,下一层的合法直线数会急剧下降,这对于这道题来说非常重要,如果你一开始就找出了所有直线,维护他们是否合法的代价就太大了,还不如每次都现找;
(2)找出所有直线后按照他们覆盖点的个数排序,当前层选取直线的时候从大到小选取,并且只取最大的5条(如果合法直线比5条多的话)。然后,第一次找到一个解,就认为这个解是最优解并退出。
这个真的是看脸,我至今都觉得这个算法不科学,只能说他对的概率不小就是了,但是拿这个算法AC简直糊我一脸……
不评论了,这个看脸的社会……
题目是说你有一个坐标系中的点阵,其中点的两个坐标范围分别为0~n和0~m,而且都是整数点。你的好基友在这坐标系上画了几条直线,每条直线保证只穿过整数点,而且至少穿过3个整数点。现在你得到了一个矩阵,矩阵描述了每个点都被穿过了多少次,你需要确定你的基友最少画了多少条直线。答案保证不超过14.
这真没什么好办法,只能搜索,但是如果真老老实实地搜,肯定会T到死……
既然要搜索,我们肯定要找到这里所有的直线,不妨先看看怎样不重不漏地找出这里所有的直线吧,我的算法是这样的:先预处理出所有不是0的点,从左向右,从上向下扫描每个点,以它为起点,然后从它开始继续从左向右,从上到下枚举每个点作为终点,如果这两个点确定的向量的两个分量不互质(例如(1,1)和(3,3)向量是(2,2)),那么肯定没戏,他们中间一定隔过了一个点,或者中间的点是0;设这个向量为vec;
然后从起点开始每次减去vec枚举,看到0就说明没戏,看到不为0的点也说明没戏,因为我是从左向右,从上到下扫描的,假如我看到一个在我上面的(或左边的)点,那么应该是这个点来找我才对,这说明这条直线已经被找过了,所以不要;
接下来从终点每次加上vec枚举,看到0说明没戏,而且要注意记录一下是否看到了至少一个1,如果是的话就ok,这就是一条满足要求的直线了。
然后说搜索,我最后AC时采用的搜索姿势是:
(1)找线的工作放在每层搜索之前做,这很重要,因为你会发现找到一条直线并划去这条直线之后,下一层的合法直线数会急剧下降,这对于这道题来说非常重要,如果你一开始就找出了所有直线,维护他们是否合法的代价就太大了,还不如每次都现找;
(2)找出所有直线后按照他们覆盖点的个数排序,当前层选取直线的时候从大到小选取,并且只取最大的5条(如果合法直线比5条多的话)。然后,第一次找到一个解,就认为这个解是最优解并退出。
这个真的是看脸,我至今都觉得这个算法不科学,只能说他对的概率不小就是了,但是拿这个算法AC简直糊我一脸……
不评论了,这个看脸的社会……
#include<iostream> #include<stdio.h> #include<string.h> #include<math.h> #include<algorithm> using namespace std; const int INF=1<<30; int n,m,t; struct Point { int x,y; Point(){} Point(int _x, int _y) { x=_x; y=_y; } void set(int _x, int _y) { x=_x; y=_y; } Point operator + (const Point &b) const { return Point(x+b.x, y+b.y); } Point operator - (const Point &b) const { return Point(x-b.x, y-b.y); } int operator ^(const Point &b) const { return x*b.y - y*b.x; } }; struct Line { Point s,e; int cover; Line(){} void set(Point _s, Point _e, int _c) { s=_s; e=_e; cover=_c; } bool operator < (const Line &b) { return cover < b.cover; } }; Point point[5000]; int p_point = 0; int map[70][70]; int min_ans = 14; bool found = false; void init() { p_point=0; memset(map, 0, sizeof(map)); min_ans = 14; found = false; return; } int gcd(int x, int y) { if(x<y) swap(x,y); if(y==0) return x; if(x%y==0) return y; return gcd(y, x%y); } bool valid(Point p) { return p.x >=0 && p.y >= 0 && p.x <=n && p.y <=m; } bool CheckLine(int p1, int p2, int &cover)//判断p1 p2组成的直线是否合法,顺便算出他们的覆盖点数 { Point now_vec = point[p2] - point[p1]; if(gcd( abs(now_vec.x), abs(now_vec.y)) != 1) return false; int now_cover = 0; Point tmp = point[p1]; tmp = tmp - now_vec; while(valid(tmp)) { if(map[tmp.x][tmp.y] == 0) return false; else if(tmp.x <= point[p1].x) return false; now_cover ++; tmp = tmp - now_vec; } tmp = point[p2]; tmp = tmp + now_vec; bool found_one = false; while(valid(tmp)) { found_one = true; if(map[tmp.x][tmp.y] == 0) return false; now_cover++; tmp = tmp + now_vec; } if(!found_one) return false; cover = now_cover; return true; } void DetermineLines(Line line[], int &p_line) { int i,j; for(i=1; i<=p_point -1;i++) { if(map[point[i].x][point[i].y] ==0) continue; for(j=i+1; j<=p_point; j++) { if(map[point[j].x][point[j].y]==0) continue; int cover; if( CheckLine(i,j, cover) ) { line[++p_line].set(point[i], point[j], cover); } } } return; } void drop(Line l) { Point p1 = l.s, p2 = l.e; Point now_vec = p2 - p1; Point tmp = p1; while(valid(tmp)) { map[tmp.x][tmp.y] --; tmp = tmp - now_vec; } tmp = p2; while(valid(tmp)) { map[tmp.x][tmp.y] --; tmp = tmp + now_vec; } return ; } void resume(Line l) { Point p1 = l.s, p2 = l.e; Point now_vec = p2 - p1; Point tmp = p1; while(valid(tmp)) { map[tmp.x][tmp.y]++; tmp = tmp - now_vec; } tmp = p2; while(valid(tmp)) { map[tmp.x][tmp.y]++; tmp = tmp + now_vec; } return ; } bool cmp(const Line &a, const Line &b) { return a.cover > b.cover; } bool check() { int i; for(i=1; i<=p_point; i++) { if(map[point[i].x][point[i].y] != 0) return false; } return true; } void DFS(int depth) { Line line[3000]; int p_line = 0; DetermineLines(line, p_line); sort(line+1, line + p_line +1, cmp); if(check()) { min_ans = depth-1; found = true; return ; } if(depth > min_ans) return; int i; for(i=1; i<=min(p_line,4);i++) { drop(line[i]); DFS(depth+1); if(found) break; resume(line[i]); } return ; } int main() { scanf("%d", &t); int files; for(files=1; files<=t; files++) { init(); scanf("%d %d", &n, &m); int i,j; for(i=0; i<=n; i++) { for(j=0; j<=m; j++) { scanf("%d", &map[i][j]); if(map[i][j]!=0) { point[++p_point].set(i,j); } } } DFS(1); printf("%d\n", min_ans); } //system("pause"); return 0; }
相关文章推荐
- HDU-5031-Lines(DFS)
- hdu 1010 dfs,奇偶剪枝
- hdu 1181 变形课(DFS)
- HDU 2614 Beat (DFS)
- HDU 1198 Farm Irrigation(dfs)
- DFS(剪枝)_hdu_1010
- HDU 5723(最小生成树+dfs)
- HDU 6060 17多校3 RXD and dividing(树+dfs)
- hdu 5678 ztr loves trees(dfs序、主席树静态第k大)
- 暴力枚举,dfs(三角形篱笆,HDU 4277)
- 【HDU 1427 计算24点 DFS】
- hdu 5952 Counting Cliques(最大团思想+dfs)
- HDU 5544 Ba Gua Zhen (DFS找环+线性基)
- 2017 Multi-University Training Contest - Team 1 1006&&HDU 6038 Function【DFS+数论】
- hdu 5014(dfs + 位运算)
- hdu 2616 Kill the monster (DFS)
- hdu 5311 Hidden String (dfs)
- HDU 1015 Safecracker (DFS)
- hdu1520(树形DP+DFS)
- 2017多校训练赛第一场 HDU 6035 Colorful Tree (dfs+正序统计)