寒假训练贪心专题

A - 外币兑换

蒜头君刚从美国回来,发现手上还有一些未用完的美金,于是想去银行兑换成人民币。可是听说最近人民币将会升值,并从金融机构得到了接下来十二个月可能的美元对人民币汇率,现在,蒜头君想要在接下来一年中把美金都兑换成人民币,请问最多能得到多少人民币?
输入格式
输入的第一行是一个实数 N(1.00 \le N \le 100.00)N(1.00≤N≤100.00),表示蒜头君现有的美金数量。
接下来一行,包含 1212 个实数 a_ia i,表示接下来十二个月的美元对人民币汇率。
输出格式
输出一个小数 RR,表示蒜头君最多能获得的人民币数量,结果保留两位小数。

直接选最大的就行。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+100;
double a[20];
int main()
{
	double n,f,k;
	cin>>n;
	double mx=0.0;
	for(int i=1;i<=12;i++){
		cin>>f;
		mx=max(mx,f);
	}
	k=n*mx;
	printf("%0.2f\n",k);
	return 0;
}

B - 删数问题

键盘输入一个高精度的正整数 nn,去掉其中任意 kk 个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的 n,kn,k,寻找一种方案使得剩下的数字组成的新数最小。
输出应包括所去掉的数字的位置和组成的新的整数。
输入格式
第一行一个高精度整数 nn,nn 不超过 200200 位。
第二行一个整数 kk,kk 小于 nn 的位数,表示要删去的数字个数。
输出格式
一个合法的整数,表示最后剩下的最小数。
用手先算的话发现 先删除从高位模拟最先递减的就是最优的。
这题要注意删除前导0

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+100;
string s;
int k,a[330];
int main()
{
	cin>>s;
	scanf("%d",&k);
	int len=s.length();
	for(int i=0;i<len;i++)
		a[i]=s[i]-'0';
	int l=0;
	for(int i=1;i<=k;i++) 
	{
		for(int i=0;i<len;i++) 
		{
			if(a[i]>a[i+1]) 
			{
				for(int j=i;j<len;j++)
					a[j]=a[j+1];
				len--;
				break;
			}
		}
	}
	int i=0; 
	while(a[i]==0 && l<len-1) {
		l++;
		i++;
	}
	for(int i=l;i<len;i++)
		printf("%d",a[i]);
	return 0;
}

C - 股票买卖

最近越来越多的人都投身股市,阿福也有点心动了。谨记着“股市有风险,入市需谨慎”,阿福决定先来研究一下简化版的股票买卖问题。
假设阿福已经准确预测出了某只股票在未来 N 天的价格,他希望买卖两次,使得获得的利润最高。为了计算简单起见,利润的计算方式为卖出的价格减去买入的价格。
同一天可以进行多次买卖。但是在第一次买入之后,必须要先卖出,然后才可以第二次买入。
现在,阿福想知道他最多可以获得多少利润。
这题思路,以某一个数位节点,求出这个数之前的利润最大值和这个数之后的利润最大值。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
int n,a[maxn],f1[maxn],f2[maxn];
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		int mn=inf,mx=-inf;
		memset(f1,0,sizeof(f1));
		memset(f2,0,sizeof(f2));
		for(int i=1;i<=n;i++)
		{
			mn=min(mn,a[i]);//找到这个数之前的最小值 
			f1[i]=max(f1[i-1],a[i]-mn);//求出以这个数为节点的最大利润 
		}
		for(int i=n;i>=1;i--){
			mx=max(mx,a[i]);
			f2[i]=max(f2[i+1],mx-a[i]);
		}
		int ans=-inf;
		for(int i=1;i<=n;i++) ans=max(ans,f1[i]+f2[i]);
		cout<<ans<<endl;
	}
	return 0;
}

D - 数列分段

对于给定的一个长度为 NN 的正整数数列 A_iA
i ,现要将其分成连续的若干段,并且每段和不超过 MM(可以等于 MM),问最少能将其分成多少段使得满足要求。
这题就是模拟,遍历一遍就行,只要某段和大于k,就新增。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
int n,k;
int a[maxn];
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int s=0,t=0;
	for(int i=1;i<=n;i++)
	{
		s+=a[i];
		if(s>k){
			s=a[i];
			t++;
		}
	}
	if(s<=k) t++;
	cout<<t<<endl;
	return 0;
}

E - 最大子段和

N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。

例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
经典问题·,只要小于0就从0重新开始 取最大的。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
int a[maxn],n;
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	ll s=0,mx=0;
	for(int i=1;i<=n;i++)
	{
		s+=a[i];
		if(s<0) s=0;
		mx=max(mx,s);
	}
	cout<<mx<<endl;
	return 0; 
}

F - 活动安排问题

有若干个活动,第i个开始时间和结束时间是[Si,fi),同一个教室安排的活动之间不能交叠,求要安排所有活动,最少需要几个教室?

对开始时间和结束时间进行升序排序,重新组成一些抽象的活动,因为这些时间段都要安排进行。
贪心:在一个会场中,最多能进行多少次活动。
当开始时间在结束时间之前,也就是开始时间大于结束时间时,一个会场就不能满足。会场数++
例如 1 2, 2 9, 3 4
生成 1 2。 2 4。3 9。只有两个活动可以进行,相当于1 2 和 3 4 进行了一样!!!

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
int n,a[maxn],b[maxn];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d %d",&a[i],&b[i]);
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	int j=1,sum=0;
	for(int i=1;i<=n;i++){
		if(a[i]<b[j]) sum++;
		else j++;
	}
	cout<<sum<<endl;
	return 0;
}

G - 线段

数轴上有 nn 条线段,选取其中 kk 条线段使得这 kk 条线段两两没有重合部分,问 kk 最大为多少。
这个题跟F题几乎一样,不过这个是按结束时间排序的,因为要尽可能的多。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e6+100;
struct node
{
	int x;
	int y;
}p[maxn];
int n;
bool cmp(node a,node b)
{
	return a.y<b.y;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d %d",&p[i].x,&p[i].y);
	sort(p+1,p+n+1,cmp);
	int end=p[1].y;
	int ans=1;
	for(int i=2;i<=n;i++){
		if(p[i].x>=end){
			ans++;
			end=p[i].y;
		}
	}
	cout<<ans<<endl;
}

H - 定价

在市场上有很多商品的定价类似于 999 元、4999 元、8999 元这样。它们和 1000 元、5000 元和 9000 元并没有什么本质区别,但是在心理学上会让人感觉便宜很多,因此也是商家常用的价格策略。不过在你看来,这种价格十分荒谬。于是你如此计算一个价格 pp(pp 为正整数)的荒谬程度:

首先将 pp 看做一个由数字组成的字符串(不带前导 00);
然后,如果 pp 的最后一个字符是 00,就去掉它。重复这一过程,直到 pp 的最后一个字符不是 00;
记 pp 的长度为 aa,如果此时 pp 的最后一位是 55,则荒谬程度为 2a - 12a−1;否则为 2a2a。
例如,850850 的荒谬程度为 33,而 880880 则为 44,99999999 的荒谬程度为 88。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
int cnt[20]={0,1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
int l,r;
int get(int T)
{
    int ret=0;
    while(T%10==0)T/=10,ret++;
    return ret;
}
int las(int T)
{
    while(T%10==0)T/=10;
    return T%10;
}
int sum(int T)
{
    int sum=0;
    while(T%10==0)T/=10;
    while(T)T/=10,sum++;
    return sum;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&l,&r);
        int ans,minn=99999999;
        for(int i=l;i<=r;i+=cnt[get(i)+1])
        {
            int op;
            if(las(i)==5)op=1;
            else op=0;
            if(minn>2*sum(i)-op)
            {
                minn=2*sum(i)-op;
                ans=i;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

I - 排队接水

n个人一起排队接水,第i个人需要b[i]的时间来接水。(1<=n<=1000,0<=b[i]<=1000)
同时只能有一个人接水,正在接水的人和没有接水的人都需要等待。
完成接水的人会立刻消失,不会继续等待。
你可以决定所有人接水的顺序,并希望最小化所有人等待时间的总和。
从小到大排就行 ,这样等的时间少些、

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
int n,a[maxn];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	ll s=0,k=0;
	for(int i=1;i<=n;i++){
		k+=a[i-1];
		s=s+k+a[i];
	}
	cout<<s<<endl;
	return 0;
}

J - 拼成最小的数 V2

设有n个正整数,将它们连接成一排,组成一个最小的多位整数。

例如:

n=2时,2个整数32,321连接成的最小整数为:32132,

n=4时,4个整数55,31,312, 33 联接成的最小整数为:312313355

这个题可以利用STL进行排列。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
string s[maxn];
int n;
bool cmp(string a,string b) 
{
	return a+b<b+a;
}
int main()
{
	cin>>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];
	cout<<endl;
	return 0;
}

K - Huffman coding tree

哈夫曼编码,可以利用STL里的优先队列,小根堆进行计算。

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e5+100;
int main()
{
	int n,x;
	priority_queue<int, vector<int>, greater<int> >q;
	scanf("%d",&n); 
	for(int i=1;i<=n;i++){
		scanf("%d",&x);
		q.push(x);
	}
	ll s=0;
	int k1,k2;
	while(q.size()>1)
	{
		k1=q.top(); q.pop();
		k2=q.top(); q.pop();
		s+=k1+k2;
		q.push(k1+k2);
	}
	cout<<s<<endl;
	return 0;
}

L - 货币系统

在网友的国度中共有 n 种不同面额的货币,第 i 种货币的面额为 a[i],你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 n、面额数组为 a[1\dots n]a[1…n] 的货币系统记作 (n,a)。

在一个完善的货币系统中,每一个非负整数的金额 x 都应该可以被表示出,即对每一个非负整数 x,都存在 n 个非负整数 t[i] 满足 a[i] \times t[i]a[i]×t[i] 的和为 x。然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 x 不能被该货币系统表示出。例如在货币系统 n=3, a=[2,5,9] 中,金额 1,3 就无法被表示出来。

两个货币系统 (n,a) 和 (m,b) 是等价的,当且仅当对于任意非负整数 x,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

现在网友们打算简化一下货币系统。他们希望找到一个货币系统 (m,b),满足 (m,b) 与原来的货币系统 (n,a) 等价,且 m 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 m。

我们来看一下题,先知道什么是货币系统:其实就是这几种不同面值的钱任意组合出其他钱。
如果一个货币系统中有3,有6,那么6就可以省略,因为6可以由两个3组成,这样我们就可以得到一个最小货币系统,这个货币系统是原来的子集,且里面每种面值都是独一无二不可代替的,与原本的是等价关系
我们可以从最小面值开始(因为最小面值肯定无法代替),然后面值依次变大
这样,题意就成了,给你一堆数,每个数可用无数次,问能组成多少数?
这不就是完全背包问题
我们先排序
然后对前i-1个货币进行完全背包,不能被取代的币值也加入背包中,到最后看看有多少

f[i]表示当前这个数x之前的数能不能组成i,如果f[x]等于1,那么说明x可以删了,删掉即可;如果f[x]是0,那么x不能删,就按完全背包的方式更新f数组。(官方题解引入)

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2e5+100;
int n,ans,a[maxn],f[maxn];
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		sort(a+1,a+n+1);
		memset(f,0,sizeof(f));
		f[0]=1;
		ans=n;
		for(int i=1;i<=n;i++)
		{
			if(f[a[i]]==1)
			{
				ans--;
				continue;
			}
			for(int j=a[i];j<=a[n];j++)
			{
				if(f[j-a[i]]) f[j]=1;
				else if(f[j]) continue;
			}
		}
		cout<<ans<<endl;
	} 
	return 0;
}

M - 反素数ant

对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数。例如,整数1,2,4,6等都是反质数。现在给定一个数N,你能求出不超过N的最大的反质数么?
这题算是数论题,可以看下y总讲解反素数

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1e6+100;
ll ps[11]={2,3,5,7,11,13,17,19,23,29};
ll n,cnt,num;
void dfs(int u,int last,ll p,ll s)//p为上一次的数 s为约数个数 
{
	if(s>cnt||s==cnt&&p<num)
	{
		cnt=s;
		num=p; 
	}
	if(u==9) return ; //前九个质数相乘就大于2e9了 
	for(int i=1;i<=last;i++)
	{
		if(p*ps[u]>n) break;
		p*=ps[u];
		dfs(u+1,i,p,s*(i+1));
	} 
}
int main()
{
	cin>>n;
	dfs(0,30,1,1);
	cout<<num<<endl;
	return 0;
}

N - 接水问题二

n 个人一起排队接水,第 ii 个人的重要性是 a[i]a[i],需要 b[i]b[i] 的时间来接水。(1 <= n <= 100000,0 <= b[i] <= 1000,0 <= a[i] <= 1000)

同时只能有一个人接水,正在接水的人和没有接水的人都需要等待。

完成接水的人会立刻消失,不会继续等待。

你可以决定所有人接水的顺序,并希望最小化所有人等待时间乘以自己的重要性 a[i]a[i] 的总和,输出等待时间乘以自己的重要性的总和。
如果x在前面,代价 a[x]b[x]+a[y](b[x]+b[y])
如果y在前面 代价a[y]b[y]+a[x](b[y]+b[x])
如果a[y]*b[x]<b[y]*a[x],x在前面,否则y在后面。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+100;
struct node
{
	int x;
	int y;
}p[maxn];
int n;
bool cmp(node a,node b)
{
	return a.x*b.y<b.x*a.y;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%d %d",&p[i].x,&p[i].y);
		if(p[i].x==0||p[i].y==0){
			i--; n--;
		}
	}
	sort(p+1,p+n+1,cmp);
	ll s=0,t=0;
	for(int i=1;i<=n;i++)
	{
		t+=p[i].x;
		s+=t*p[i].y;
	}
	printf("%lld\n",s);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值