题目描述简单,阅读无障碍。用贪心求解。
贪心很容易迷惑人,一不小心就贪错了。我刚开始的想法。
1、对所有的岛,按x坐标从小到大排序
2、沿x轴从左向右开始,找到第一个还没有被覆盖的岛。以该岛为圆心、雷达覆盖范围d为半径画圆。判断圆与x轴的交点,若与x轴没有交点,则无解(因为该岛无论如何也覆盖不到),若有交点,则继续。
3、以右边的交点作为雷达安装点,安装雷达,覆盖所有可以覆盖的点。
4、回到第二步
5、如果所有的岛都被覆盖了,则结束,此时已经安装的雷达总数就是最后的解。
这种想法其实是错误的。这种每次都把雷达尽力往靠x轴右边安装,看似是最优的,其实不是。那只是雷达每次能按照的最右位置,而非最优位置。“过犹不及”,有时往左一点更好。
正确的做法是这样的。
1、对每个岛,以该岛为圆心、雷达覆盖范围d为半径画圆。这个圆与x轴相交,会到一段区间。于是,所有的点都被转化为x轴上对应的区间。(呃,貌似有点像活动选择问题)
2、对每个岛在x轴上对应的区间,只要在这个区间内安装雷达,该岛就可以被覆盖到。沿x轴从左向右看,区间之间的关系无非有三种。
若两个区间不相交,则需要两个雷达,每个区间安装一个
若区间有公共部分,则需要一个雷达,按照在左区间的右端点
若一个区间在另一个区间内,则需要一个雷达,按照在小区间的右端点
3、沿x轴从左向右,当处理完所有的区间时,此时的雷达总数就是最后的解
这种做法和算法导论上的活动选择问题有点像,活动选择是在一段时间内尽可能多的安排不冲突的活动。本题是根据区间之间的相互关系,尽可能的使用最少的雷达覆盖所有的岛,慢慢体会吧。
POJ2054 color a tree
贪心问题。要使着色时间最少,最直观的想法是:使着色因子最大的点尽早的被着色。光有这点意识还不够,解决这题的关键,是要发现:着色因子最大的点一定是紧跟着它的父节点之后被着色的。严格的证明这里就讨论了,不过可以简单的理解一下:着色因子最大的点应该在可以着色的时候(即父节点之后被着色后)尽快着色。
基于上述观察,可以考虑把色因子最大的点和它的父节点合并。合并后的点着色因子是多少?原来节点着色因子的平均值。这里的平均值,是指两个被合并的集合的Ci值之和除以两个集合的大小之和,譬如两个集合A,B,A的Ci和为Ca,大小为Ta,B的Ci和为Cb,大小为Tb,则合并后为(Ca+Cb)/(Ta+Tb)。(想一想为什么?)
还要几道贪心的题目,在这里也推荐一下,相对比较简单,也可以做做,似乎更适合入门。
POJ1230 Pass Murielle
POJ1017 packets
POJ2709 painter
POJ1328
#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
struct Range // the range for radar installation in the x-axis so that the radar can cover the island
// the range can be got from the circle which the center is an island position in the x-y coordinates
{
float left;
float right;
};
bool CompareRange( const Range& first, const Range& second ) // sort ranges with the left, and arrange them from small to large
{
return first.left < second.left;
}
int GetNumByGreedy( const vector<Range>& islands ) // caculate the minimum radar number ( Assume: islands is not empty )
{
int size = islands.size();
if( size == 1 )
return 1;
int radarNum = 1;
float pre = islands[0].right;
for( int i=1; i<size; i++ )
{
if( islands[i].left > pre ) // if the new range have no commond with the previous range, we need to add an extra radar
{
pre = islands[i].right;
radarNum++;
}
else
{
if( islands[i].right < pre ) // if they have commond range, we need to find out it
pre = islands[i].right;
}
}
return radarNum;
}
int main()
{
vector<int> result; // save the minimum radar number for each input case
int islandNum;
int radarRange;
while( cin >> islandNum >> radarRange )
{
if( islandNum == 0 && radarRange == 0 ) // end of input
break;
vector<Range> islands; // save the range in the x-axis for the each island
bool impossible = false;
for( int i=0; i<islandNum; i++ )
{
int x, y; // (x,y) represents the position of island in the x-y coordinates
cin >> x >> y;
if( y > radarRange ) // in this case, no solution for radar installation
impossible = true;
if( !impossible )
{
float tmp = sqrt( (float)(radarRange*radarRange - y*y) );
Range rg;
rg.left = x - tmp;
rg.right = x + tmp;
islands.push_back( rg );
}
}
if( impossible )
{
result.push_back( -1 );
continue;
}
sort( islands.begin(), islands.end(), CompareRange );
int rlt = GetNumByGreedy( islands );
result.push_back( rlt ); // save the minimum radar number for each input case
}
for( int i=0; i<result.size(); i++ )
cout << "Case " << i+1 << ": " << result[i] << endl;
return 0;
}
POJ2054
#include <iostream>
#include <vector>
using namespace std;
#define NUM 1002
struct Node
{
bool exist; //表示节点是否已经被删除
int parent;
double cost;
vector<int> squence; //合并后的节点序列
};
int nodeNum;
int rootIndex;
int costArray[NUM];
Node tree[NUM];
int GetMaxCost() //找到cost最大的节点的索引
{
int result;
double max = 0;
for( int i=1; i<=nodeNum; i++ )
{
if( tree[i].exist && ( tree[i].cost > max ) ) //节点存在,并且不是根节点
{
max = tree[i].cost;
result = i;
}
}
return result;
}
int Solve()
{
int index;
for( int i=1; i<nodeNum; i++ ) //有nodeNum个节点,则合并nodeNum-1次,最后可剩一个节点
{
int max = GetMaxCost(); //找到cost最大的节点的索引
int parent = tree[max].parent;
int maxCount = (int)tree[max].squence.size();
int parentCount = (int)tree[parent].squence.size();
double sum = tree[parent].cost * parentCount + tree[max].cost * maxCount;
tree[parent].cost = sum / ( maxCount + parentCount ); //更新父节点的索引(注意:这里不能简单的相加除2)
tree[parent].squence.insert( tree[parent].squence.end(), tree[max].squence.begin(), tree[max].squence.end() );
tree[max].squence.clear();
for( int j=1; j<=nodeNum; j++ ) //修改最大cost节点的所有孩子的父节点
{
if( tree[j].exist && tree[j].parent == max )
tree[j].parent = parent;
}
tree[max].exist = false; //删除子节点
index = parent;
}
return index;
}
void Output( int index )
{
long sum = 0;
for( int i=0; i< (int)(tree[index].squence.size()); i++ )
{
int temp = tree[index].squence.at( i );
sum += costArray[temp] * ( i+1 );
}
tree[index].squence.clear();
printf( "%d\n", sum );
}
int main()
{
while( scanf( "%d%d", &nodeNum, &rootIndex ), !( nodeNum == 0 && rootIndex == 0 ) )
{
memset( tree, 0, sizeof(tree) );
memset( costArray, 0, sizeof(costArray) );
for( int i=1; i<=nodeNum; i++ ) //输入各节点的costFactor
{
scanf( "%d", &costArray[i] );
tree[i].cost = costArray[i];
tree[i].squence.push_back( i );
tree[i].exist = true;
}
tree[rootIndex].exist = false; //设置根节点的父节点
for( int j=1; j<nodeNum; j++ ) //根据边的情况,填充父子关系
{
int parent, child;
scanf( "%d%d", &parent, &child );
tree[child].parent = parent;
}
int x = Solve();
Output( x );
}
return 0;
}