几道贪心题目 POJ1328 radar installation POJ2054 color a tree

来源:互联网 发布:关于人工智能的弊端 编辑:程序博客网 时间:2024/05/21 06:55
POJ1328 radar installation

题目描述简单,阅读无障碍。用贪心求解。

贪心很容易迷惑人,一不小心就贪错了。我刚开始的想法。

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 NUM1002struct 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;}