从零单排3

7. 贪心算法经典题目

做了大概两个小时的贪心题目,从杭电的贪心课件中找了六题来做,除了田忌赛马那个题太绕了意外其他题目还是比较轻松的

这几到简单的贪心题目总结下来就是排序+分析贪心性质

以下是题解..(话说前面那个装逼的扫雷男好烦...)


hdu 1009:http://acm.hdu.edu.cn/showproblem.php?pid=1009

/*
简单的贪心
复习一下cmp写法 
*/
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct JavaBeans
{
	double j;
	double f;
};
JavaBeans a[1005];
bool cmp(const JavaBeans &a,const JavaBeans &b)
{
	return a.j/a.f>b.j/b.f;
}

int main()
{
	double m;
	int n;
	while(cin>>m>>n,m!=-1&&n!=-1)
	{
		for(int i=0;i<n;i++)
		{
			cin>>a[i].j>>a[i].f;
		}
		sort(a,a+n,cmp);
		double sum=0;
		for(int i=0;i<n;i++)
		{
			if(m<a[i].f)
			{
				sum+=m*a[i].j/a[i].f;
				break;
			}
			sum+=a[i].j;
			m-=a[i].f;
		}
		printf("%.3lf\n",sum);
	}
	system("pause");
	return 0;
}

hdu 1789: http://acm.hdu.edu.cn/showproblem.php?pid=1789

/*
贪心
我的做法是输入按照分数高的排在前,分数相同的按照deadline靠前的在前排序
然后从头开始处理数据,开一个b[]数组对应相应的deadline,
如果b[i]=0说明这天还没有作业,则把b[i]赋为1并总分数减去a[i]
如果b[i]=1说明这天已经安排了分数更大的作业,则向前遍历找到还没有安排作业的一天,
1A~
*/
#include<iostream>
#include<algorithm>
using namespace std;
struct work
{
	int day;
	int score;
};
work a[1005];
int b[1005];
bool cmp(const work &a,const work &b)
{
	if(a.score==b.score)
	{
		return a.day<b.day;
	}
	else
	{
		return a.score>b.score;
	}
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		int sum=0;
		memset(b,0,sizeof(b));
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i].day;
		}
		for(int i=1;i<=n;i++)
		{
			cin>>a[i].score;
			sum+=a[i].score;
		}
		sort(a+1,a+n+1,cmp);
		for(int i=1;i<=n;i++)
		{
			if(b[a[i].day]==0)
			{
				b[a[i].day]=1;
				sum-=a[i].score;
			}
			else
			{
				for(int j=a[i].day-1;j>=1;j--)
				{
					if(b[j]==0)
					{
						b[j]=1;
						sum-=a[i].score;
						break;
					}
				}
			}
		}
		cout<<sum<<endl;
	}
	system("pause");
	return 0;
}

hdu 1051: http://acm.hdu.edu.cn/showproblem.php?pid=1051

/*
这题一开始纠结了好久cmp该怎么排序...
第一关键字为l,第二关键字为w
然后从0开始扫描,用last记录起始位置,用used[]数组标记是否扫描过
*/
#include<iostream>
#include<algorithm>
using namespace std;
struct wood
{
	int l;
	int w;
};
wood a[5005];
wood last;
bool cmp(const wood &a,const wood &b)
{
	if(a.l==b.l)
	{
		return a.w<=b.w;
	}
	else
	{
		return a.l<b.l;
	}
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int used[5005];
		memset(used,0,sizeof(used));
		int n;
		cin>>n;
		for(int i=0;i<n;i++)
		{
			cin>>a[i].l>>a[i].w;
		}
		sort(a,a+n,cmp);
		int count=0;
		for(int i=0;i<n;i++)
		{
			last=a[i];
			if(!used[i])
			{
				for(int j=i+1;j<n;j++)
				{
					if(a[j].w>=last.w&&!used[j])
					{
						used[j]=1;
						last=a[j];
					}
				}
				count++;
			}
		}
		cout<<count<<endl;
	}
	system("pause");
	return 0;
}
	

hdu 2037: http://acm.hdu.edu.cn/showproblem.php?pid=2037

/*
简单贪心
按结束时间从小到大排序
依次选择开始时间晚于上一次结束时间的活动加入
*/
#include<iostream>
#include<algorithm>
using namespace std;
struct tv
{
	int begin;
	int end;
};
tv a[105];
tv last;
bool cmp(const tv &a,const tv &b)
{
	if(a.end==b.end)
	{
		return a.begin<b.begin;
	}
	else
	{
		return a.end<b.end;
	}
}
int main()
{
	int n;
	while(cin>>n&&n)
	{
		int count=1;
		for(int i=0;i<n;i++)
		{
			cin>>a[i].begin>>a[i].end;
		}
		sort(a,a+n,cmp);
		last=a[0];
		for(int i=1;i<n;i++)
		{
			if(a[i].begin>=last.end)
			{
				last=a[i];
				count++;
			}
		}
		cout<<count<<endl;
	}
	system("pause");
	return 0;
}
			
	
h du 1050:http://acm.hdu.edu.cn/showproblem.php?pid=1050

/*
贪心 
这个题以前做过印象比较深
因为1-2 3-4。。。并不占用其他走廊其实
所以将两边走廊合到一边
压缩到hash[201]中
然后每占用a-b区间的走廊就分别+1
最后统计最大值就好
注意输入的a,b并不一定满足a<b,这里wa了好几次
*/
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(const int &a,const int &b)
{
	return a>b;
}
int hash[201];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		cin>>n;
		memset(hash,0,sizeof(hash));
		while(n--)
		{
			int a,b;
			cin>>a>>b;
			if(a%2==0)
			{
				a=a/2;
			}
			else
			{
				a=(a+1)/2;
			}
			if(b%2==0)
			{
				b=b/2;
			}
			else
			{
				b=(b+1)/2;
			}
			if(a>b)
			{
				swap(a,b);
			}
			for(int i=a;i<=b;i++)
			{
				hash[i]++;
			}
		}
		sort(hash+1,hash+201,cmp);
		cout<<hash[1]*10<<endl;
	}
	system("pause");
	return 0;
}

hdu 1052: http://acm.hdu.edu.cn/showproblem.php?pid=1052

/*
这个题作为目前贪心练习的最后一题
好烦的推理。。
1.当田忌最慢的马比齐王最慢的马快,赢一场先
2.当田忌最慢的马比齐王最慢的马慢,和齐王最快的马比,输一场
3.当田忌最快的马比齐王最快的马快时,赢一场先。
4.当田忌最快的马比齐王最快的马慢时,拿最慢的马和齐王最快的马比,输一场。
5.当田忌最快的马和齐王最快的马相等时,拿最慢的马来和齐王最快的马比.

田忌赛马贪心的正确性证明。

先说简单状况下的证明:
1.当田忌最慢的马比齐王最慢的马快,赢一场先。因为始终要赢齐王最慢的马,不如用最没用的马来赢它。
2.当田忌最慢的马比齐王最慢的马慢,和齐王最快的马比,输一场。因为田忌最慢的马始终要输的,不如用它来消耗齐王最有用的马。
3.当田忌最慢的和齐王最慢的马慢相等时,分4和5讨论。
4.当田忌最快的马比齐王最快的马快时,赢一场先。因为最快的马的用途就是来赢别人快的马,别人慢的马什么马都能赢。
5.当田忌最快的马比齐王最快的马慢时,拿最慢的马和齐王最快的马比,输一场,因为反正要输一场,不如拿最没用的马输。
6.当田忌最快的马和齐王最快的马相等时,这就要展开讨论了,贪心方法是,拿最慢的马来和齐王最快的马比.
前面的证明像公理样的,大家一看都能认同的,没有异议的,就不细说了。


证明:田忌最快的马和齐王最快的马相等时拿最慢的马来和齐王最快的马比有最优解。

1)假设他们有n匹马,看n=2的时候.

a1 a2
b1 b2

因为 田忌最快的马和齐王最快的马相等 所以a1=b1,a2=b2 所以这种情况有2种比赛方式,易得这两种方式得分相等。

2)当数列a和数列b全部相等等时(a1=b1,a2=b2...an=bn),显然最慢的马来和齐王最快的马比有最优解,可以赢n-1长,输1场,找不到更好的方

法了。
3)当数列a和数列b元素全部相等时(a1=b1=a2=b2...=an=bn),无法赢也不输。

现在假设n匹马时拿最慢的马来和齐王最快的马比有最优解,证明有n+1匹马时拿最慢的马来和齐王最快的马比也有最优解。

数列
a1 a2 a3 a4...an an+1
b1 b2 b3 b4...bn bn+1

其中ai>=ai-1,bi>=bi-1

数列a和数列b不全部相等时,拿最慢的马来和齐王最快的马比数列得到数列
(a1) a2 a3 a4...an an+1
b1 b2 b3 b4...bn (bn+1)

分4种情况讨论 
1.b1=b2,an=an+1
则有
a2 a3 a4...an
b2 b3 b4...bn
其中a2>=a1,a1=b1,b1=b2,得a2>=b2(此后这种大小关系不再论述),an>=bn.
此时若a2=b1,根据归纳假设,有最优解,否则a2>根据前面“公理”论证有最优解。
当且仅当a数列,b数列元素全部相等时有an+1=b1,已证得,所以an+1>b1,赢回最慢的马来和齐王最快的马比输的那一场。

2.b1<=b2,an=an+1
交换 b1,b2的位置,
数列
(a1) a2 a3 a4...an an+1
b2 b1 b3 b4...bn (bn+1)
此时 a2>=a1,an>=bn,
对于子表
a2 a3 a4...an
b1 b3 b4...bn
根据前面“公理”或归纳假设,有最优解。
an+1>=b2, 当且仅当b2=b3=b4=..=bn+1时有an+1=b2,这种情况,a中其它元素<=b1,b2,b3,b4..bn,对于这部分来说,能赢 x盘(x<=n),假如不拿最慢的马来和齐王最快的马比则拿最快的马来和齐王最快的马比,此时平一盘,能赢x-1盘,而拿最慢的马来和齐王最快的马 比,输一盘能赢x盘,总的来说,还是X这个数,没有亏。

3.b1=b2,an<=an+1
4.b1<=b2,an<=an+1证明方法类似,不再重复。

以证得当有n+1匹马的时候,田忌和齐王最快最慢的马速度相等时,拿最慢的马来和齐王最快的马比有最优解,已知当n=2时成立,所以对于n>2且为整数(废话,马的只数当然是整数)时也成立。当n=1时....这个似乎不用讨论.
wa了好多次...从网上搜到了判定条件。。
*/
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int K[1005],T[1005];
int n;
int main() {

    while (1)
    {
        scanf("%d",&n);
        if (n==0) break;
        for (int i=0;i<n;i++) scanf("%d",&T[i]);
        for (int i=0;i<n;i++) scanf("%d",&K[i]);
        sort(T,T+n);
        sort(K,K+n);
        int j=0;
        int win=0;
        int kn=n;
        for (int i=0;i<n;)
        {
            if(T[i]>K[j])
            {
                i++;
                win++;
                j++;
            }
            else if(T[i]<K[j])
            {
                kn--;
                i++;
                win--;
            }
            else if(T[i]==K[j])
            {
                if (T[n-1]>K[kn-1])
                {
                    n--;
                    kn--;
                    win++;
                }
                else if(T[n-1]<K[kn-1])
                {
                    i++;
                    kn--;
                    win--;
                }
                else if(T[n-1]==K[kn-1])
                {
                    if (T[i]<K[kn-1])
                      win--;
                    kn--;
                    i++;
                }
            }
        }
        printf("%d\n",win*200);
    }
}

话说刚刚收到了武大的电话,通知去参加周日的全国信息搜索大赛决赛...我在想这比赛到底是有多水。。。初赛复赛各答10题就能晋级30人的决赛了么。。。

虽说奖品很诱人还有什么百度offer还有报销路费住宿费。。。但确实太远太麻烦了。。。而且这么轻松的比赛其含金量也是呵呵了。。。

虽然妹子的声音蛮好听的。。


明天整理一下大一上的内容,周末从大一下冲起~!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值