贪心算法经典习题7道

HDOJ-1257 最少拦截系统

【题目】有一种带缺陷的导弹拦截系统,它的第一发炮弹能够到达任意的高度,之后每一发炮弹都不能超过前一发的高度。现给出导弹依次飞来的高度,计算一下最少需要多少套拦截系统。
【思路】
对每个炮弹进行处理:
当前高度低于或等于其中一些系统:选择这些系统中高度最低的,该系统高度=当前高度
当前高度高于所有系统:系统数+1,该系统高度=当前高度

#include
#define INF 30005
int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		int t,hm,i,im=0,s[105]={0},mi;
		while(n--){
			scanf("%d",&t);
			hm=INF;
			for(i=1;i<=im;i++)
				if(s[i]>=t&&s[i]<hm)
					{hm=s[i];mi=i;}
			if(hm<INF) s[mi]=t;
			else {im++;s[im]=t;}
		}
		printf("%d\n",im);
	}
	return 0;
}

Upgrading Technology

【题目】玩家在游戏里有n种属性,每种属性都有m(1≤n, m≤1000)个等级,编号1~m。在开始的时候,每种属性都处于等级0。当第i种属性在第j-1等级时,玩家可以花c[i][j] (-109≤c[i][j]≤109)的代价来把它升级到第j等级。当所有属性都升级到等级j时,玩家可以得到d[j] (-109≤d[j]≤109)的满级奖励。求玩家可以获得的最大收益。
【思路】
想法:外循环从0到m枚举所有属性的最低等级j,中循环为每种属性,内循环枚举其升到j~m级的代价,找出最小代价。
漏洞:如果所有属性都在j+1(也可以是j+2,…,m)级取得最小代价,那么就会违背所有属性的最低等级为j的前提,答案就会错误。
修正:在所有属性取得最小代价之后,选择一种属性降级到j,这样会使总代价变大,称降级带来的代价变化为“损失”,选择损失最小的属性降级。在中循环执行时统计每种属性的降级损失。

#include
#define LL long long
const int N=1000+5;
LL inf=1e9*N;
LL cc[N][N],dd[N];
int main(){
    int t,ca;
    scanf("%d",&ca);
    for(t=1;t<=ca;t++){
        int i,j,k,temp;LL pre,ma;
        int n,m;//n为属性种数,m为最高等级
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++){
            pre=cc[i][0]=0;
            for(j=1;j<=m;j++){
                scanf("%d",&temp);
                pre=pre-temp;
                cc[i][j]=pre;//cc[i][j]为单独把属性i升到j级的代价
            }
        }
        pre=dd[0]=0;
        for(j=1;j<=m;j++){
            scanf("%d",&temp);
            pre=pre+temp;
            dd[j]=pre;//dd[j]为把所有属性升级到j级的累计满级奖励
        }
        LL loss,sum,ans=0;
        for(j=0;j<=m;j++){//最低的等级为j
            sum=dd[j];
            loss=inf;
            for(i=1;i<=n;i++){
                sum+=cc[i][j];
                ma=0;
                for(k=j+1;k<=m;k++)
                    if(cc[i][k]-cc[i][j]>ma) ma=cc[i][k]-cc[i][j];
                sum+=ma;
                if(ma<loss) loss=ma;
            }
            sum-=loss;
            if(sum>ans) ans=sum;
        }
        printf("Case #%d: %lld\n",t,ans);
    }
    return 0;
}

HDOJ-2037 今年暑假不AC

【题目】输入电视节目时间表,合理安排观看顺序,输出最多能完整看到的电视节目的个数。
【思路】最好的选择是剩余可安排时间最多,即结束最早。对电视节目按照结束时间排序,按播出时间从早到晚选择节目,如果时间不重叠,则入选。
【证明】如果按开始时间排序,有可能开始时间早、持续时间长、单个,比不上开始时间晚、持续时间短、多个,如1-6,2-4,4-5。反之,如果按结束时间排序,不可能出现结束时间早、持续时间长、单个。

#include
#include
#include
using namespace std;
struct jiemu
	{int s;int e;}jm[105];
bool cmp(struct jiemu x,struct jiemu y)
	{if(x.e!=y.e)return x.e<y.e;
	return x.s>y.s;}
int main(){
	int n,i,s,t;
	while(scanf("%d",&n)!=EOF&&n){
		for(i=0;i<n;i++)
			{cin>>jm[i].s;cin>>jm[i].e;}
		sort(jm,jm+n,cmp);
		s=0;t=0;
		for(i=0;i<n;i++)
			if(jm[i].s>=t)
				{t=jm[i].e;s++;}
		cout<<s<<endl;
	}
	return 0;
}

UVA 11134 - Fabled Rooks

【题目】在 n∗n 的棋盘上放 n(n≤5000)个车,使得任意两个车不相互攻击,且第 i 个车在一个给定的矩形 Ri之内(包括边界)。 用 4个整数 xli,yli,xri,yri描述第 i个矩形,其中(xli,yli)是左上角坐标,(xri,yri)(xri,yri)是右下角坐标。如果无解,输出 IMPOSSIBLE; 否则,输出 n行,依次为第 1,2,…,n1,2,…,n 个车的坐标。
【思路】
N皇后问题中由于存在对角线(主、副)约束,使用DFS较为简便;本问题中每个车的可用区域为矩形,最坏情况为
∏ i = 1 n ( x r i − x l i ) ( y r i − y l i ) \prod_{i=1}^{n}{(x_{ri}-x_{li})(y_{ri}-y_{li})} i=1n(xrixli)(yriyli)
考虑使用贪心产生不冲突方案。将行、列分开处理,以棋盘的每行(列)作为主体和顺序。
想法一:将区间按照左端点从小到大排序,然后对每个区间,从左到右找到第一个未被放置的点。如果大区间包含小区间,而大区间先被处理,取到大区间和小区间相交的地方,后续处理小区间时,小区间内所有点可能已被之前的大区间取完。所以在这种情况下,需要优先处理小区间。
想法二:把区间按照右端点从小到大排序,然后对每个区间,从左到右找到第一个未被放置的点。这样,确保越靠右的区间处理次序越靠后。

#include
#include
#include
using namespace std;
const int N=5000;int n;
int bx[5010],by[5010];
//存储棋盘上占用第i行(列)的车的编号,0代表空 
struct r//存储格子编号和端点
{int l,r,num;}rx[N+10],ry[N+10];
bool cmp(r a,r b) 
{if(a.r!=b.r) return a.r<b.r;
else return a.l<b.l;}
bool solve(){
    for(int i=1;i<=n;i++){//对于排序后的每个格子
        int j=rx[i].l;
		while(bx[j]) j++;
		if(j>rx[i].r) return false;
		else bx[j]=rx[i].num;
    }
    for(int i=1;i<=n;i++){
        int j=ry[i].l;
		while(by[j]) j++;
		if(j>ry[i].r) return false;
		else by[j]=ry[i].num;
    }
    return true;
}
void print(){
	int ans[N+10][2]={0};
	for(int i=1;i<=n;i++)
		{ans[bx[i]][0]=i;ans[by[i]][1]=i;}
	for(int i=1;i<=n;i++)
		printf("%d %d\n",ans[i][0],ans[i][1]);
}
int main(){
    while(scanf("%d",&n)!=EOF&&n){
        for(int i=1;i<=n;i++){
            scanf("%d%d%d%d",&rx[i].l,&ry[i].l,&rx[i].r,&ry[i].r);
            rx[i].num=ry[i].num=i;
        }
        sort(rx+1,rx+n+1,cmp);sort(ry+1,ry+n+1,cmp);
        memset(bx,0,sizeof(bx));memset(by,0,sizeof(by));
        if(solve()) print();
		else printf("IMPOSSIBLE\n");
    }
    return 0;
}

HDOJ-1009 FatMouse’ Trade

【题目】肥鼠准备了M磅猫粮,准备和谷仓的看守猫交换Java豆。谷仓有N个房间,第i个房间储藏着J[i]磅Java豆,全部交换需要F[i]磅猫粮。肥鼠可以按比例与猫交易。输入房间的信息,求肥鼠最多可以得到多少Java豆。
【思路】题目要达到的最终目的是用有限的猫粮交换到尽可能的Java豆,即投入产出比最大化。进一步分解,可以求出每个房间的投入产出比,进行降序排列,求得最优解。

#include 
#include 
using namespace std;//使用了C++头文件
struct room{
	double bean;
	double qian;
	double bili;
}r[1005];
bool cmp(room x,room y)
	{return x.bili>y.bili;}//降序排列
int main(){
	int m,n,i;
	double sum;
	while(scanf("%d%d",&m,&n)&&m!=-1&&n!=-1){
		for(i=0;i<n;i++)
			{scanf("%lf%lf",&r[i].bean,&r[i].qian);
			r[i].bili=r[i].bean/r[i].qian;
			//单位猫粮可以得到的Java豆}
		sort(r,r+n,cmp);
		sum=0.0;
		for(i=0;i<n;i++){
			if(m>r[i].qian) //全部交换
				{sum+=r[i].bean;m-=r[i].qian;}
			else {sum+=m*r[i].bili;break;}
		}
		printf("%.3lf\n",sum);
	}
	return 0;
}

HDOJ-1052 Tian Ji – The Horse Racing

【题目】现代的田忌赛马规则如下:田忌与齐王赛马,赢一场可以得到200元钱,输一场则要损失200元钱。双方各有最多1000匹马,输入各对马匹的速度,输出田忌最多可以赢的钱。
【思路】要达到利益最大化,就要使每对匹配最值,即在一定条件下,以最大差距输,以最小差距赢。为此要先对双方马匹按速度排序。

#include
#include
int main(){
	int i,n,a[1005],b[1005],amin,amax,bmin,bmax,s;
	while(cin>>n && n){
		for(i=1;i<=n;i++) cin>>a[i];//a代表田忌
		for(i=1;i<=n;i++) cin>>b[i]; //b代表齐王
		sort(a+1,a+n+1);
		sort(b+1,b+n+1);// 田忌与齐王的马从慢到快排序
		amin=1;amax=n;
		bmin=1;bmax=n;
		s=0;
		if(a[amax]>b[bmax])//先对付最快的
		{s+=200;amax--;bmax--;}//用最快直接打(比最快慢的也许可行,最快省事)
		else if(a[amax]<b[bmax]) //用最慢直接打
		{s-=200;amin++;bmax--;}
			else//反向对付最慢的
				if(a[amin]>b[bmin]) //用最慢直接打
					{s+=200;amin++;bmin++;}
			else{//用最慢打最快
				if(a[amin]<b[bmin]) s-=200;
				//平局钱不变
				amin++;bmax--;
			}
		cout>>s>>endl;
	}
	return 0;
}

HDOJ-1050 Moving Tables

【题目】在一个狭窄的走廊里将桌子从一个房间移动到另一个房间,走廊的宽度只能允许一张桌子通过。现在要移动n张桌子,每移动一张桌子需要花10分钟,移动过程中起点、终点与途经的房间不可用。输入移桌任务,输出最少花费的时间。
【思路】用数组P的下标,表示楼道的格子,400个房间对应200个格子。用s表示起点房间,d表示终点房间,则(s-1)/2是开始格子,(d-1)/2是目的格子。从开始格子到目的格子,每个格子的值都加1,表示桌子经过这些房间1次,占用一次时间。每经过一次都加1,格子值即为占用次数。如果每张按照路线中的最小值确定搬运时间,则用时最短。所以,最后把最大的值找出来乘以10就得到最短时间。

#include 
using namespace std; 
int main() { 
	int t,i,j,N,P[200];//P存储各个楼道格子的值
	int s,d,temp,k,min; 
	cin>>t; 
	for(i=0;i<t;i++) { 
		for(j=0;j<200;j++) //P初始化
			P[j]=0; 
		cin>>N; 
		for(j=0;j<N;j++) { 
			cin>>s>>d; 
			s=(s-1)/2; 
			d=(d-1)/2;                
			if(s>d)//循环变量递增,要确保起点<终点
				{temp=s;s=d;d=temp;} 
			for(k=s;k<=d;k++) 
				P[k]++; 
		} 
		min=-1; 
		for(j=0;j<200;j++) 
			if(P[j]>min) 
				min=P[j]; 
		cout<<min*10<<endl; 
	} 
	return 0; 
}

你可能感兴趣的:(贪心算法,算法)