• 贪心算法


    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("
    %.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;
    } 
    
  • 相关阅读:
    SVM理论之线性分类
    编写自己的sniffer(二)
    二叉树三种非递归遍历的区别
    SVM理论之最优超平面
    [转载] Linux的capability深入分析
    [转载] ftp的模式ACTIVE&PASSIVE
    Linux下压缩不包含路径信息的压缩包
    [ZzDW] 关于Java对象序列化您不知道的5件事
    [转载] Windows如何在cmd命令行中查看、修改、删除与添加、设置环境变量
    [攻略转载] 在飞机上睡觉的七大攻略
  • 原文地址:https://www.cnblogs.com/ying-xue/p/tan-xin-suan-fa.html
Copyright © 2020-2023  润新知