贪心算法

P1223 排队接水

要使得后面等待的时间少,那么就是要尽量让接水时间短的人排在前面。按照接水所需时间从小到大排序,即为接水的顺序。

注意算平均值的时候不要把最后一个人的接水时间也加上。

\(AC code\)

#include<bits/stdc++.h>
using namespace std;
long long a,b[1005],c[1005];
double d;
int main()
{
	scanf("%lld",&a);
	for(int i=1;i<=a;i++)
	{
		scanf("%lld",&b[i]);
		c[i]=c[i-1]+1;
	}
	for(int i=1;i<=a;i++)
	{
		for(int j=1;j<a;j++)
		{
		    if(b[j]>b[j+1])
		    {
		    	swap(b[j],b[j+1]);
		    	swap(c[j],c[j+1]);
		    }
		}
	}
	for(int i=1;i<=a;i++)
	{
		printf("%lld ",c[i]);
		
	}
	for(int i=1;i<a;i++)
	{
		b[i]+=b[i-1];
	}
	for(int i=1;i<a;i++)
	{
		b[i]+=b[i-1];
	}
	d=(double)b[a-1]/a;
	printf("\n%.2lf",d);
    return 0;
}

P1012 [NOIP1998 提高组] 拼数

第一想法是按从大到小排序拼在一起,但是分析下样例二很快就发现不对(所以貌似白想了好久

\(a,b,c,d\) 为字符串。设 \(a>=b\) 当且仅当 \(a+b>=b+a\) 。所以最优排列就是把这几个数按 \(>=\) 排列就好了,因为显然如果其中相邻的两个数不满足前面 \(>=\) 后面,那么把它们两个换一下显然数会更大。不会证明所以就这样吧(逃

\(ACcode\)

#include<bits/stdc++.h>
using namespace std;
string s[25];
int n;
bool cmp(string a,string b){
	return a+b>b+a;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) cin>>s[i];
	sort(s+1,s+n+1,cmp);
	for(int i=1;i<=n;i++) cout<<s[i];
	return 0;
}

P1094 [NOIP2007 普及组] 纪念品分组

贪心的方案:

  • 如果最大的和最小的能配对,则配成对,否则最大的单独成对,直到全部配对为止。

正确性的证明(个人理解,并不保证严谨):

  • 如果该数和最小的数配对的值大于 \(w\) ,那么它和剩下任何数配对的值都一定大于 \(w\) ,因此它只能自己成一组。

  • 如果最大的 \(a_r\) 不与最小的 \(a_l\) 配对,而与 \(a_i\) 配对, \(a_l\)\(a_j\) 配对,因为 \(a_l<=a_i\)\(a_r>=a_j\) ,所以交换两者分组不影响后续选择。

  • 故此贪心正确。

\(ACcode\)

#include<bits/stdc++.h>
using namespace std;
int n,w;
int a[30001],ans,l,r;
int main()
{
    scanf("%d%d",&w,&n);
    l=1;r=n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    sort(a+1,a+n+1);
    while(l<=r)
    {
        if(a[l]+a[r]<=w) 
        {
            l++;r--;ans++;
        }
        else
        {
            r--;ans++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

P1090 [NOIP2004 提高组] 合并果子

贪心。

每次取最小的两堆合并,最后即为正确答案。(不会证明/wq)

所以说主要问题就是怎么找最小的两堆。

由于中间不断有插入和删除,所以用优先队列。

\(ACcode\)

#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;
int n,a[10001];
long long ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		q.push(a[i]);
	}
	while(q.size()>=2)
	{
		int x=q.top();q.pop();
		int y=q.top();q.pop();
		q.push(x+y);
		ans+=x+y;
	}
	cout<<ans;
}

P1968 美元汇率

受纪念品那题的启发,同一天买入再卖出等于没买。所以问题转化为决定每天是否要买入或卖出。为使最后答案最大,就是要当 a 比较大的时候买进来,比较小的时候卖出去。因此,如果 \(a_i>a_{i+1}\) ,那么在第 \(i\) 天换成马克,在 \(i+1\) 天再换成美元就是赚钱的。以后的每天以此类推,即为最优解。

#include<bits/stdc++.h>
using namespace std;
int n,a[101];
double ans=100;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=1;i<n;i++)
	{
		if(a[i]>a[i+1]) ans=ans*a[i]/a[i+1];
	}
	printf("%.2lf",ans);
	return 0;
}

P1233 木棍加工 (即零件分组)

贪心和 dp 都可以做,这里用的是贪心。

因为要同时考虑 \(l\)\(w\) 两个数据的排序,所以不妨先将 \(l\) 进行升序排序再单独考虑 \(w\)

对于每一个还没有被分入其它组的木棍,往后一个个循环,将能放进来的都放进来。最后统计没有加入任何组的木棍数,即为答案。

\(ACcode:\)

#include<bits/stdc++.h>
using namespace std;
int n,now,ans;
struct stick{
	int w,l;
}a[5005];
bool cmp(stick x,stick y)
{
	if(x.l==y.l) return x.w<y.w;
	return x.l<y.l;
}
bool vis[5005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i].l,&a[i].w);
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			now=a[i].w;
			ans++;
			for(int j=i+1;j<=n;j++)
			{
				if(a[j].w>=now&&!vis[j])
				{
					now=a[j].w;
					vis[j]=1;
				}
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

P5761 [NOI1997] 最佳游览

刚开始理解错题意了卡了好久交了四遍才过掉(

因为每一列都可以随便选,所以当然是每一列选最大值。先一遍读入一遍纵向扫出每一列的最大值,储存在 \(a\) 数组中。

注意起点和终点不一定是两端。 所以再在 \(a\) 数组上跑一遍最大子段和板子就好了...

最后的结果把 \(f\) 数组扫一遍取最大值。

写的过程中踩的坑:

  • 看错了数据范围
  • 理解错了题意
  • dp 数组不赋初值

总之一塌糊涂/kk

#include<bits/stdc++.h>
using namespace std;
const int inf=2e9;
int m,n;
int s[105][20005],a[20005];
int f[20005],ans;
int main()
{
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;i++){a[i]=-inf;f[i]=-inf;ans=-inf;}
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<n;j++)
		{
			scanf("%d",&s[i][j]);
			a[j]=max(a[j],s[i][j]);
		}
	}
	//for(int i=1;i<n;i++) cout<<a[i]<<" ";
	for(int i=1;i<n;i++)
	{
		f[i]=max(f[i-1]+a[i],a[i]);
		ans=max(ans,f[i]);
	}
	
	cout<<ans;
	return 0;
}

P2095 营养膳食

按脂肪含量排序,然后一个个扫一遍判断是否超出限制,没有的话就吃掉。

#include<bits/stdc++.h>
using namespace std;
int n,m,k,num[110],pan[110]={0},ok=1,ans=0; 
struct node{
    int a,b;
}s[210];
bool cmp(node x,node y){
	return x.a<y.a;
}
int main()
{ 
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++) scanf("%d",&num[i]);
    for(int i=1;i<=n;i++) scanf("%d%d",&s[i].a,&s[i].b);
    sort(s+1,s+n+1,cmp);
    for(int i=n;i;i--)  
    {
    	if(ok>m) break; 
        if(pan[s[i].b]<num[s[i].b])
        {
            ans+=s[i].a;           
            ok++;pan[s[i].b]++;
        }
    }
    cout<<ans<<endl;
    return 0;
} 
posted @ 2021-04-03 23:29  樱雪喵  阅读(115)  评论(0编辑  收藏  举报