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;
}