20220313模拟赛总结

考前复习图论结果发现题目是贪心

1,排队接水

【问题描述】

有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序, 使得n个人的平均等待时间最小。

【输入格式】

输入文件共两行,第一行为n;第二行分别表示第 1 个人到第n个人每人的接水时间T1,T2,…,Tn,每 个数据之间有 1 个空格。

【输出格式】

输出文件有两行,第一行为一种排队顺序,即1到n 的一种排列;第二行为这种排列方案下的平均等 待时间(输出结果精确到小数点后两位)。

【输入样例】

10 3 2 7 8 1 4 9 6 10 5 56 12 1 99 1000 234 33 55 99 812

【输出样例】

532.00

贪心很显然,接水时间短的先接。

输出样例似乎和洛谷有亿点点不一样,一顿乱试发现自己接水的时间也算等待时间……

校内某OJ用快读tle用cin就过也离谱。

#include
using namespace std;
const int N=1005;
int n;
long long ans;
struct qwq{
	int t,num;
}a[N];
bool cmp(qwq x,qwq y){
	if(x.t!=y.t) return x.t>n;
	for(int i=1;i<=n;i++) cin>>a[i].t,a[i].num=i;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++){
		printf("%d ",a[i].num);
		ans+=(n-i+1)*a[i].t;
	}
	double res=ans*1.0/n;
	printf("\n%.2lf",res);
	return 0;
}

2、最大整数(Noip1998 连接多位数)

【问题描述】

设有 n 个正整数(n≤20),将它们联接成一排,组成一个最大的多位整数。 例如:n=3 时,3 个整数 13,312,343 联接成的最大整数为:34331213 又如:n=4 时,4 个整数 7,13,4,246 联接成的最大整数为:7424613

【输入格式】

n

n 个数

【输出格式】

联接成的多位数

【输入样例】

3 13 312 343

【输出样例】

34331213

第一眼贪心依旧显然,靠前的数最高位要大 ,若最高位相同则顺次比较第二位第三位……

但若是123 1234 12345呢?543 5432 54321呢?比较比麻了。

这时想到了字符串模拟,string中的大于号和加号可以让我们瞬间比较出来谁前谁后哪个更优。

借助c++党得天独厚的优势sort,这道题就完美结束了。

#include
using namespace std;
inline int read(){
	register int x=0,f=1;
	register char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
string s[25];
int n; 
bool cmp(string a,string b) {
	return a+b>b+a;
}
int main() {
	n=read();
	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<

3、纪念品分组(NOIP2007)

【题目描述】

元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得的纪念 品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品,并且每组纪念品的价格之和不能超过一个给定的整数。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。 你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。

【输入格式】

输入文件 group.in 包含 n+2 行: 第 1 行包括一个整数 w,为每组纪念品价格之和的上限。 第 2 行为一个整数 n,表示购来的纪念品的总件数。 第 3~n+2 行每行包含一个正整数pi (5 <= pi <= w),表示所对应纪念品的价格。

【输出格式】

输出文件 group.out 仅一行,包含一个整数,即最少的分组数目。

【输入样例】

100 9 90 20 20 30 50 60 70 80 90

【输出样例】

6

【限制】

50%的数据满足:1 <= n <= 15; 100%的数据满足:1 <= n <= 30000, 80 <= w <= 200

 既然善良的让我们每组最多选两个,那么显然每次看当前最大的和当前最小的能不能分在一组就好了。

#include
using namespace std;
inline int read(){
	register int x=0,f=1;
	register char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=30005;
int w,n,a[N],ans=0;
inline bool cmp(int x,int y){return x>y;}
int main(){
	w=read(),n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	sort(a+1,a+n+1,cmp);
	for(int i=1,j=n;i<=j;i++){
		if(a[i]+a[j]<=w) j--;
		ans++;
	}
	printf("%d",ans);
	return 0;
}

4、合并果子(Noip2004)

【问题描述】

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定 把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所 有的果子经过 n-1 次合并之后,就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体 力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重 量都为 1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的 体力最少,并输出这个最小的体力耗费值。

例如有 3 种果子,数目依次为 1,2,9。可以先将 1、2 堆合并,新堆数目为 3,耗费体力为 3。接着, 将新堆与原先的第三堆合并,又得到新的堆,数目为 12,耗费体力为 12。所以多多总共耗费体力=3+12=15。 可以证明 15 为最小的体力耗费值。

【输入文件】

输入文件fruit.in包括两行,第一行是一个整数n(1 <= n <= 10000),表示果子的种类数。第二行 包含n个整数,用空格分隔,第i个整数ai(1 <= ai <= 20000)是第i种果子的数目。

【输出文件】

输出文件fruit.out包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这 个值小于 231 。

【样例输入】

3 1 2 9

【样例输出】

15

【数据规模】

对于 30%的数据,保证有 n <= 1000; 对于 50%的数据,保证有 n <= 5000; 对于全部的数据,保证有 n <= 10000。

很经典的题目,显然每次合并的果子都是当前体力耗费值最小的两堆。

所以接着领取c++党的福利,浅用一下优先队列吧。

注意当队列中只有一个元素就跳出循环,而不是队列为空。

#include
using namespace std;
inline int read(){
	register int x=0,f=1;
	register char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,a,ans;
priority_queue,greater >q;
int main(){
	n=read();
	for(int i=1;i<=n;i++) a=read(),q.push(a);
	while(q.size()!=1){
		int x=q.top();q.pop();
		int y=q.top();q.pop();
		ans+=x+y;
		q.push(x+y);
	}
	printf("%d",ans);
	return 0;
}

5、美元汇率

【问题描述】

在以后的若干天里戴维将学习美元与德国马克的汇率。编写程序帮助戴维何时应买或卖马克或美元, 使他从 100 美元开始,最后能获得最高可能的价值。

【输入格式】

输入文件的第一行是一个自然数 N,1≤N≤100,表示戴维学习汇率的天数。 接下来的 N 行中每行是一个自然数 A,1≤A≤1000。第 i+1 行的 A 表示预先知道的第 i+1 天的平均汇 率,在这一天中,戴维既能用 100 美元买 A 马克也能用 A 马克购买 100 美元。

【输出格式】

输出文件的第一行也是唯一的一行应输出要求的钱数(单位为美元,保留两位小数)。 注意:考虑到实数算术运算中进位的误差,结果在正确结果 0.05 美元范围内的被认为是正确的,戴维 必须在最后一天结束之前将他的钱都换成美元。

【输入样例】

5 400 300 500 300 250

【输出样例】

266.66

【样例解释】 (无需输出)

Day 1 ... changing 100.0000 美元= 400.0000 马克

Day 2 ... changing 400.0000 马克= 133.3333 美元

Day 3 ... changing 133.3333 美元= 666.6666 马克

Day 5 ... changing 666.6666 马克= 266.6666 美元

考虑动态规划,令f[i][0]为第i天最多拥有的美元,f[i][1]为第i天最多拥有的马克,w[i]为第i天汇率,则小明在第1天最多有100美元,w[1] 马克。

在推状态转移方程式之前,我们先发现:对于任意一天换钱,要么不换,要么全都换。这是因为:若亏损,显然不换最优;若盈利,显然全都换盈利最多。所以不可能存在换一部分留一部分的情况。

那么之后的每一天,对于美元,小明可以选择马克换美元(f[i][0]=f[i-1][1]/100*w[i])与不换(f[i][0]=f[i-1][0]);对于马克,小明可以选择美元换马克(f[i][1]=f[i-1][0]/w[i]*100)与不换(f[i][1]=f[i-1][1])。以上两种情况分别取最大。

#include
using namespace std;
inline int read(){
	register int x=0,f=1;
	register char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e6+2;
int n,a[N];
double f[N][2];
using namespace std;
int main(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    f[1][0]=100;
    f[1][1]=a[1];
    for(int i=2;i<=n;i++){
        f[i][0]=max(f[i-1][0],(f[i-1][1]/a[i])*100);
        f[i][1]=max(f[i-1][1],f[i-1][0]*a[i]/100);
    }
    printf("%.2lf",max(f[n][0],(f[n][1]/a[n])*100));
    return 0;
}

6、零件分组

【问题描述】

某工厂生产一批棍状零件,每个零件都有一定的长度(Li)和重量(Wi).现在为了加工需要,要将它们分成若干组,使每一组的零件都能排成一个长度和重量都不下降(若i

【输入格式】

第一行为一个整数N(N<=1000),表示零件的个数。第二行有N对正整数,每对正整数表示这些零件的长度和重量,长度和重量均不超过10000.

【输出格式】

仅一行,即最少分成的组数。

【输入样例】

5

8 4 3 8 2 3 9 7 3 5

【输出样例】

2

浅看一下很简单,按长度非降序排序,长度相同按重量非降序排序,扫一遍看有几个w[i]

举个栗子:当排序之后为

根据原先设计的算法要分三组,但实际上只用两组就够了。怎么办呢?

于是很神的hhs大佬(%%%)想到了解决方案:

设一个布尔数组vis,当vis[i]=1则表示i的重量是i当前组的最大重量。则对于每一个i,从w[i]向前枚举每一个j,当找到第一个vis[j]=1,将i加入当前组,并将vis[j]置为0。由于不论i加入已有组还是加入新的一组,i的重量都一定是当前组中最大值,故循环结束后应将vis[w[i]]置为1。

由于每一组只有一个最大重量,所以vis[i]=1的i的数量即为组数。

#include
using namespace std;
inline int read(){
	register int x=0,f=1;
	register char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e3+5;
int n,maxx;
bool vis[N];
struct qwq{
	int l,w;
}a[N];
inline bool cmp(qwq x,qwq y){
	if(x.l!=y.l) return x.l0;j--){
			if(vis[j]){vis[j]=0;break;} 
		}
		vis[a[i].w]=1;
	}
	int ans=0;
	for(int i=1;i<=maxx;i++) if(vis[i]) ans++;
	printf("%d",ans);
	return 0;
}

7、运输

【问题描述】

现在已知 N 件商品。和搬运它们其中每一件的费用。现在搬家公司的老板 Mr.B 决定让 我们每次任意选取 2 件商品。然后这 2 件商品只算一件商品的费用。但是这个商品的搬运费 用是将选出的 2 个商品的费用之和除以 K 的运算结果。如此反复。直到只收一件商品的钱。 这个就是商店要付的费用。想尽可能的少付钱,以便将更多的钱卷给希望工程。所以请你帮 他计算一下最少只用付多少钱。

【输入格式】

n,k

w1,w2,…,wn(每一件商品的搬运费用)

【输出格式】

输出一个数字,表示最少付多少钱。

【输入样例】

5 2 1 2 3 4 5

【输出样例】

1

【数据规模】

n<=10000 k<=10000 

咱就说这不是合并果子君变身么。

#include
using namespace std;
inline int read(){
	register int x=0,f=1;
	register char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e4+5;
int n,k,w;
priority_queue q;
int main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++) w=read(),q.push(w);
	while(q.size()!=1){
		int x=q.top();q.pop();
		int y=q.top();q.pop();
		q.push((x+y)/k);
	}
	printf("%d",q.top());
	return 0;
}

8、最佳游览线路(Noi1994)

【题目描述】

某旅游区的街道成网格状。其中东西向的街道都是旅游街,南北向的街道都是林荫道。 由于游客众多,旅游街被规定为单行道,游客在旅游街上只能从西向东走,在林阴道上则既 可从南向北走,也可以从北向南走。

阿龙想到这个旅游区游玩。他的好友阿福给了他一些建议,用分值表示所有旅游街相邻 两个路口之间的街道值得游览的程度,分值时从-100 到 100 的整数,所有林阴道不打分。 所有分值不可能全是负分。

例如图是被打过分的某旅游区的街道图:

20220313模拟赛总结_第1张图片

阿龙可以从任一个路口开始游览,在任一个路口结束游览。请你写一个程序,帮助阿龙 找一条最佳的游览线路,使得这条线路的所有分值总和最大。

【输入格式】

第一行是两个整数 M 和 N,之间用一个空格符隔开,M 表示有多少条旅游街(1≦M≦ 100),N 表示有多少条林阴道(1≦M≦20001)。接下来的 M 行依次给出了由北向南每条旅 游街的分值信息。每行有 N-1 个整数,依次表示了自西向东旅游街每一小段的分值。同一 行相邻两个数之间用一个空格隔开。

【输出格式】

只有一行,是一个整数,表示你的程序找到的最佳游览线路的总分值。

【输入样例】

 3 6

-50 -47 36 -30 -23 17 -19 -34 -13 -8 -42 -3 -43 34 -45

【输出样例】

84

对于每一列上的数,找出第i列的最大值mp[i]。令f[i]为前i条路的最大总分值,由于可以从任意位置开始走,所以有:f[i]=max(f[i-1]+mp[i],mp[i])

又由于可以从任意位置结束游览,故要在f[1]到f[n]中再次取最大值。

再次感谢hhs大佬对本dp大蒟蒻真诚无私耐心热情的帮助。

#include
using namespace std;
inline int read(){
	register int x=0,f=1;
	register char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
} 
const int N=20005,inf=-1e9;
int n,m,ans;
int tu[102][N],mp[N],f[N];
int main(){
	m=read(),n=read();
	for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) tu[i][j]=read();
	for(int i=1;i<=n;i++){
		int maxn=-1e9;
		for(int j=1;j<=m;j++){
			maxn=max(maxn,tu[j][i]);
		}
		mp[i]=maxn;
	}
	for(int i=1;i<=n;i++){f[i]=max(f[i-1]+mp[i],mp[i]);}
	int ans=0;
	for(int i=1;i<=n;i++) ans=max(ans,f[i]);
	printf("%d",ans);
	return 0;
}

9、营养膳食

【问题描述】

阿月正在女朋友宁宁的监督下完成自己的增肥计划。

为了增肥,阿月希望吃到更多的脂肪。然而也不能只吃高脂肪食品,那样的话就会导致 缺少其他营养。阿月通过研究发现:真正的营养膳食规定某类食品不宜一次性吃超过若干份。 比如就一顿饭来说,肉类不宜吃超过 1 份,鱼类不宜吃超过 1 份,蛋类不宜吃超过 1 份,蔬菜类不宜吃超过 2 份。阿月想要在营养膳食的情况下吃到更多的脂肪,当然阿月的食量也是有限的。

【输入格式】

第一行包含三个正整数 n(n≤200),m(m≤100)和 k(k≤100)。表示阿月每顿饭最 多可以吃 m 份食品,同时有 n 种食品供阿月选择,而这 n 种食品分为 k 类。第二行包含 k 个不超过 10 的正整数,表示可以吃 1 到 k 类食品的最大份数。接下来 n 行每行包括 2 个正 整数,分别表示该食品的脂肪指数 ai 和所属的类别 bi,其中 ai≤100,bi≤k。

【输出格式】

一个数字即阿月可以吃到的最大脂肪指数和。

【样例输入】

6 6 3

3 3 2 15 1 15 2 10 2 15 2 10 2 5 3

【样例输出】

60

这道题跟前面的题比就很简单了,在符合不超过总数及不超过每种食品的最大份数的条件下从多到少选取。

#include
using namespace std;
inline int read(){
	register int x=0,f=1;
	register char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,k,num[105],maxx[105];
struct qwq{
	int a,b;
}x[205];
inline bool cmp(qwq x,qwq y){
	return x.a>y.a;
}
int main(){
	n=read(),m=read(),k=read();
	for(int i=1;i<=k;i++) maxx[i]=read();
	for(int i=1;i<=n;i++) x[i].a=read(),x[i].b=read();
	sort(x+1,x+n+1,cmp);
	int ans=0,cnt=0;
	for(int i=1;i<=n;i++){
		int lei=x[i].b;
		if(num[lei]

 通过贪心模拟我成功意识到了自己dp学的太差。

你可能感兴趣的:(模拟总结,c++,算法,贪心算法)