这道题非常好,不仅用到了把复杂问题分解为若干个熟悉的简单问题的方法,更是考察了对贪心法的理解和运用是否到位。

首先,如果直接在二维的棋盘上考虑怎么放不好弄,那么注意到x和y无关(因为两个车完全可以在同一条斜线上,这点和皇后问题不一样),那么就可以分别考虑两个一维的问题:这是一种区间选点问题,在每个区间里都只选一个点,最后这些点分别是1到n。这就联想到这样一个经典的贪心法解决的区间选点问题:数轴上有n个闭区间[ai,bi],选取尽量少的点,使得每个区间都至少含有一个点。这个问题的解决方法就是把所有区间按b从小到大排序(b相同时a从大到小排)。然后第一个区间选最右边的点(这个点影响到的区间最多,而且第一个区间肯定是要选的,那么既然怎么也是选,那我就选最好的那个。这就是贪心法的思维),后边的也按这种想法就行了(已经被前面的点影响到的区间就不用再选点了)。

本题的区间选点也可以用贪心的方法来做,但是以下两种生搬上面经典问题解法的方法是错误的:

一.把所有区间按左端排序,然后每次选能选的最左边的。

  反例:[1,1],[1,3],[2,2];

二.由上面的例子看出:区间的长度也有影响,不能只看左端的顺序。那么如果先安排长度短的区间,如果区间长度相同再每次选最左边的行不行呢?

  也是不行滴。。。反例:[1,1],[2,3],[3,4],[1,3].如果按这种思路选,那么最后一个位置就没有可选的了,结论是输出IMPOSSIBLE,但实际上很明显最后可以选好。

那么问题来了:为什么这两种思路不对?不就是按照那个经典问题的路子走的吗?

其实原因在于对贪心法的理解不到位,贪心法是每步选择局部最优解,然后一步一步向目标迈进。这个“目标”两字很关键,说明贪心法是目标导向的,每一步的方向一定是目标。那么我上面两种方法其实只是在模仿那个经典问题的模式,但是却没有时刻注意到这个问题最终目标是实现从1到n每一位都能放上满足条件的车,比如第二个反例最后一个格最后都无法放车了,就是因为前面没有按照对最终目标的影响效果去选择局部最优解,单纯的选最左边一个是毫无道理的,因为本题已经不是那个经典的选最少点的问题了。

正确的贪心法应该是这样:

从1到n一个格一个格的选车放,每步选择的最优区间是:该区间以前没选过,包含这个格,而且右端点是所有没选过的区间里最小的,那么我选择这个区间就最大程度的防止了以后的格子没得选(因为右端点选的是最小的)。

大致思路就是这样,在具体的代码实现中,我的做法是先按右端点排好序,然后按格选。看了github上的紫书答案,是不事先排序而每次都扫一遍然后找出最小的右端点,这种方式的时间复杂的稍高了一些,实际测试情况也是如此。

下面是我的代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<cctype>
#include<sstream>
using namespace std;
#define INF 1000000000
#define eps 1e-8
#define pii pair<int,int>
#define LL long long int
#define maxn 5000+5
int n;
bool can;
struct node
{
int id,lx,ly,rx,ry;
int ansx,ansy,usedx,usedy;
}rook[maxn];
bool cmp_x(node a,node b)
{
return a.rx<b.rx;
}
bool cmp_y(node a,node b)
{
return a.ry<b.ry;
}
bool cmp_id(node a,node b)
{
return a.id<b.id;
}
int main()
{
while(scanf("%d",&n)==&&n)
{
for(int i=;i<n;i++)
{
rook[i].usedx=rook[i].usedy=;
rook[i].id=i+;
scanf("%d%d%d%d",&rook[i].lx,&rook[i].ly,&rook[i].rx,&rook[i].ry);
}
sort(rook,rook+n,cmp_x);
for(int i=;i<=n;i++)//一格一格的看
{
can=;
for(int j=;j<n;j++)
{
if(rook[j].usedx==&&rook[j].lx<=i)
{
if(rook[j].rx<i){break;}
rook[j].usedx=;
rook[j].ansx=i;
can=;
break;
}
}
if(can==)
break;
}
if(can==) printf("IMPOSSIBLE\n");
else
{
sort(rook,rook+n,cmp_y);
for(int i=;i<=n;i++)
{
can=;
for(int j=;j<n;j++)//同样是一格一格的看
{
if(rook[j].usedy==&&rook[j].ly<=i)
{
if(rook[j].ry<i){break;}
rook[j].usedy=;
rook[j].ansy=i;
can=;
break;
}
}
if(can==) break;
}
if(can==) printf("IMPOSSIBLE\n");
else
{
sort(rook,rook+n,cmp_id);
for(int i=;i<n;i++)
{
printf("%d %d\n",rook[i].ansx,rook[i].ansy);
}
}
}
}
return ;
}

时间是0.079s

下面是github上的答案代码:

// UVa11134 Fabled Rooks
// Rujia Liu
#include<cstdio>
#include<cstring>
#include <algorithm>
using namespace std; // solve 1-D problem: find c so that a[i] <= c[i] <= b[i] (0 <= i < n)
bool solve(int *a, int *b, int *c, int n) {
fill(c, c+n, -);
for(int col = ; col <= n; col++) {
// find a rook with smalleset b that is not yet assigned
int rook = -, minb = n+;
for(int i = ; i < n; i++)
if(c[i] < && b[i] < minb && col >= a[i]) { rook = i; minb = b[i]; }
if(rook < || col > minb) return false;
c[rook] = col;
}
return true;
} const int maxn = + ;
int n, x1[maxn], yy1[maxn], x2[maxn], y2[maxn], x[maxn], y[maxn]; int main() {
while(scanf("%d", &n) == && n) {
for (int i = ; i < n; i++)
scanf("%d%d%d%d", &x1[i], &yy1[i], &x2[i], &y2[i]);
if(solve(x1, x2, x, n) && solve(yy1, y2, y, n))
for (int i = ; i < n; i++) printf("%d %d\n", x[i], y[i]);
else
printf("IMPOSSIBLE\n");
}
return ;
}

时间是0.292s

uva11134 - Fabled Rooks(问题分解,贪心法)的更多相关文章

  1. UVA - 11134 Fabled Rooks问题分解,贪心

    题目:点击打开题目链接 思路:为了满足所有的车不能相互攻击,就要保证所有的车不同行不同列,于是可以发现,行与列是无关的,因此题目可以拆解为两个一维问题,即在区间[1-n]之间选择n个不同的整数,使得第 ...

  2. 01_传说中的车(Fabled Rooks UVa 11134 贪心问题)

    问题来源:刘汝佳<算法竞赛入门经典--训练指南> P81: 问题描述:你的任务是在n*n(1<=n<=5000)的棋盘上放n辆车,使得任意两辆车不相互攻击,且第i辆车在一个给定 ...

  3. UVA-11134 Fabled Rooks 贪心问题(区间贪心)

    题目链接:https://cn.vjudge.net/problem/UVA-11134 题意 在 n*n 的棋盘上,放上 n 个车(ju).使得这 n 个车互相不攻击,即任意两个车不在同一行.同一列 ...

  4. Uva11134 Fabled Rooks

    普通的贪心题. 虽然图是二维的,但可以把横向和纵向分开处理. 将区间按右端点排序,然后从区间左端点到右端点找第一个空位置放棋子即可. /*by SilverN*/ #include<algori ...

  5. UVa 11134 - Fabled Rooks 优先队列,贪心 难度: 0

    题目 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&a ...

  6. UVA - 11134 Fabled Rooks[贪心 问题分解]

    UVA - 11134 Fabled Rooks We would like to place n rooks, 1 ≤ n ≤ 5000, on a n × n board subject to t ...

  7. 【uva 11134】Fabled Rooks(算法效率--问题分解+贪心)

    题意:要求在一个N*N的棋盘上放N个车,使得它们所在的行和列均不同,而且分别处于第 i 个矩形中. 解法:问题分解+贪心. 由于行.列不相关,所以可以先把行和列均不同的问题分解为2个"在区间 ...

  8. L - Fabled Rooks(中途相遇法和贪心)

    Problem F: Fabled Rooks We would like to place n rooks, 1 ≤ n ≤ 5000, on a n×n board subject to the ...

  9. UVA 11134 Fabled Rooks(贪心的妙用+memset误用警示)

    题目链接: https://cn.vjudge.net/problem/UVA-11134 /* 问题 输入棋盘的规模和车的数量n(1=<n<=5000),接着输入n辆车的所能在的矩阵的范 ...

随机推荐

  1. 电信、网通、联通等恶意DNS劫持跳广告页面的解决方法

    中国电信.网通.联通ADSL用户必读:中国电信.网通.联通劫持dns(中国电信.网通.联通劫持ie浏览器)解决方案D... 宽带连接有 也能上网但是本地连接一直显示为受限制的解决方法 我的电脑一直显示 ...

  2. STL之map、set灵活使用

    1.LA 5908/UVA1517 Tracking RFIDs 题意:给出s个传感器的位置,以及其感应范围.如果某个方向上有墙,则该方向上感应距离减1.现在有w个墙,给出p个物品的位置,问其能被几个 ...

  3. classmethod和staticmethod区别

    实例方法:在类中,定义的方法,这个方法的第一个参数默认是实例对象,一般习惯使用self 类方法:在类中,定义的方法,这个方法的第一个参数默认是类对象,一般习惯用cls表示,用@classmethod装 ...

  4. sublime text C++

    几乎每一门编程语言都是从"Hello, world!"学起的, 刚学编程的时候感觉有点枯燥, 对它不够重视. 可是到后来慢慢发现, 几乎我学到的每一个知识点, 在最开始都是经过 h ...

  5. OpenGL学习进程(8)第六课:点、边和图形(三)绘制图形

    本节是OpenGL学习的第六个课时,下面介绍OpenGL图形的相关知识:     (1)多边形的概念: 多边形是由多条线段首尾相连而形成的闭合区域.OpenGL规定,一个多边形必须是一个“凸多边形”. ...

  6. 转载:ensemble计划和数据库

    原文来源:x2yline在生信进化树上的评论,http://www.biotrainee.com/thread-626-1-1.html Ensemble( ensembl.org网站是常用真核生物参 ...

  7. [RK3288][Android6.0] TS-ADC驱动流程小结【转】

    本文转载自:https://blog.csdn.net/kris_fei/article/details/55045936 Platform: RK3288OS: Android 6.0Kernel: ...

  8. SpringBoot Mybatis 入门

    Mybatis for Java API官方文档:http://www.mybatis.org/mybatis-3/zh/java-api.html Mybatis语法介绍 @Select 查询,所有 ...

  9. Linux mysql主从同步配置

    一.在两台Ubuntu机器上安装mysql1.检查系统中是否安装了mysql 这个是已经安装了的 没有安装的话执行上条命令===============================MySQL的一些 ...

  10. java 反射机制复习笔记。

    1.java 反射的作用(好处): (1)能够使我们很方便的创建灵活的代码,这些代码可以在运行时装配(运行时获取一个类实例),就不用在组件之间进行源代码链接了,大大提高系统的灵活性和扩展性. (2)与 ...