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

本文介绍了贪心算法在解决POJ1328雷达安装和POJ2054树着色问题中的应用。错误的贪心策略是将雷达尽可能安装在右侧,而正确的方法是将问题转化为活动选择问题,考虑区间间的覆盖。对于树着色问题,关键在于识别着色因子最大的点应在父节点之后尽快着色,并通过合并节点来优化策略。
摘要由CSDN通过智能技术生成
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 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;
}



 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值