SMZX十日游(第一阶段模拟赛)

本次模拟赛包括四道题目,涉及背包问题、贪心算法、数论及图论,强调细节处理如边界条件、数据类型选择及正确输出格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

创作背景

目前是SMZX十日游的第3天,是应该有一场模拟赛了……分数:XX

题目清单(AC的题)

  • 小明的书包(bigbag)
  • 邮票(post)
  • 握手(shake)
  • 海狸先生,排队!(ekg)

小明的书包(bigbag)

题目描述

小明有一个很大的书包,容量为c。但大家也知道,现在的课本也很重,以至于小明没有办法一次性带上所有的课本。小明把课本的重量和重要性告诉你,请你帮他算算选择哪些课本能使得重要性之和最大。

输入格式:bigbag.in

第一行有两个正整数n和c,代表课本的数量和书包的容量。 接下来n行,每行有2个正整数wi和vi,分别代表该课本的重量和重要性。

输出格式:bigbag.out

一行一个数,输出最大可能的重要性之和。

输入样例1:

5 12
10 4
2 1
9 5
4 2
5 4

输出样例1:

7

输入样例2:

5 120000000
100000000 4
20000000 1
90000000 5
40000000 2
50000000 4

输出样例2:

7

数据范围:

对于60%的数据,背包总容量和课本重量<=100
对于100%的数据,n <= 100,课本重要性<=100,背包总容量和课本重量<=10^9

解题过程

看题,这只是一个普普通通的 01背包,然后华丽丽地 码完了01背包模板

#include<bits/stdc++.h> 
using namespace std;
signed long long n,W,f[120000005],w[105],v[105];
int main()
{
	freopen("bigbag.in","r",stdin);
	freopen("bigbag.out","w",stdout);
	cin>>n>>W;
	for(int i=1;i<=n;i++)
	    cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++)
		for(int j=W;j>=w[i];j--)
			f[j]=max(f[j],f[j-w[i]]+v[i]);
	cout<<f[W];
   return 0;   
}

然而0分,爆空间。
毕竟本题100%的数据是1亿,肯定不能直接暴力开数组空间
所以正解当然是……
f [ j ] = m i n ( f [ j ] , f [ j − v [ i ] ] + w [ i ] ) ; f[j]=min(f[j],f[j-v[i]]+w[i]); f[j]=min(f[j],f[jv[i]]+w[i]);
首先,如果是用开始的动态转移方程,那么10^9*100=一百亿,分分钟TLE警告
然后,数组也开不起,分分钟MLE警告
如果是求最小重量上面的问题就迎刃而解了
但重要的是:
1.要初始化为最大值
2.筛选时要定好阈值

Code

#include<bits/stdc++.h> 
using namespace std;
long long n,V,W,f[1000009],w[105],v[105],ans;
int main()
{
	freopen("bigbag.in","r",stdin);
	freopen("bigbag.out","w",stdout);
	cin>>n>>W;
	for(int i=1;i<=n;i++)
	{
		cin>>w[i]>>v[i];
		V+=v[i];		//统计总钱数
	}
	for(int i=1;i<=V;i++)
		f[i]=1e9;      //初始化
	for(int i=1;i<=n;i++)
		for(int j=V;j>=v[i];j--)
			f[j]=min(f[j],f[j-v[i]]+w[i]);
	for(int i=V;i>=1;i--)
		if(f[i]<=W)      //由大到小,不超过背包容量的就可以return 0;了
		{
			cout<<i;	
			return 0; 
		}
}

邮票(post)

题目描述

市面上总共有N张不同的邮票,编号0至N-1。第i张邮票的价格是p[i]。你的目标是能收集尽量多的不同邮票。如果你有足够多的钱,那显然不是问题,全部买回来就行了。但是你手头上一分钱也没钱,幸运的是一开始你手头上已经有m张邮票了,这些邮票的编号保存在数组b[i],0<=i<m。于是你决定通过卖掉你手头上的某些邮票,再买进一些其他的邮票,通过这样卖出买进,你最后最多可以有几张邮票(当然你也可以不做任何买卖)?注意:对于任意的0<=j<N, 如果你卖出邮票j,那么你可以得到p[j]金钱,如果你想买进邮票j,那么你得付p[j]金钱。

输入格式:post.in

第一行,一个整数N,表示有N张邮票。1<=N<=50。
第二行, N个整数,第i个整数表示p[i],1<=p[i]<=1000000。
第三行,一个整数m,表示你现在已经有了m张邮票。0<=m<=n.
第四行, m个整数,第i个整数表示b[i]。

输出格式:post.out

一个整数,通过买卖,你最多可以有几张邮票?

输入样例1:

4
13 10 14 20
4
3 0 2 1

输出样例1:

4
换行

样例解释

一开始你已经有第0、1、2、3张邮票了,也就是你已经拥有所有的邮票了,不需要做交易了。

输入样例2:

5
4 13 9 1 5
3
1 3 2

输出样例2:

4
换行

样例解释

你可以把第2张邮票卖掉,得到9元钱,再买进第0张邮票和第4张邮票。

数据范围:

对于60%的数据,背包总容量和课本重量<=100
对于100%的数据,n <= 100,课本重要性<=100,背包总容量和课本重量<=10^9

解题过程

看题,贪心问题一枚,只要将邮票换成钱,毕竟手头上有钱,事情才好办 ,然后排序,买下最便宜的邮票即可。

#include<bits/stdc++.h> 
using namespace std;
int n,m,p[55],b[55],ans=0,sum=0;
int main()
{
	freopen("post.in","r",stdin);
	freopen("post.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>p[i];
	cin>>m;
	for(int i=1;i<=m;i++)
	    cin>>b[i];
	for(int i=1;i<=m;i++)
	ans+=p[i];
	sort(p+1,p+n+1);
	for(int i=1;i<=n;i++)
	{
		if(ans>=p[i])
		{
			sum++;
			ans-=p[i];
		}
	}
	cout<<sum;
 return 0;   
}

是不是感觉没有毛病,但是 0 分 警 告 0分警告 0
为什么,看看输出的2个大字没有:
换 行 换行
所以说:要多打换行答案不规范,亲人两行泪
而且,这里自身的邮票的对应价值是商人的邮票的序号,不是直接对应。例如样例1中,3号邮票是14元,不是13元。不会只有我一个蒟蒻理解错了吧
最重要的是:这里是从0号开始,所以要从0输入或+1

Code

#include<bits/stdc++.h> 
using namespace std;
int n,m,p[55],b[55],ans=0,sum=0;
int main()
{
	freopen("post.in","r",stdin);
	freopen("post.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>p[i];
	cin>>m;
	for(int i=1;i<=m;i++)
	    cin>>b[i];
	for(int i=1;i<=m;i++)
	ans+=p[b[i]+1];	//算卖掉得到的钱的总数
	sort(p+1,p+n+1);//快速排序
	for(int i=1;i<=n;i++)
	{
		if(ans>=p[i])//判断买不买得起
		{
			ans-=p[i];
			sum++;
		}
	}
	cout<<sum<<endl;
 return 0;   
}

握手(shake)

题目描述

有N个人(N是偶数,不超过50),坐在一个园桌旁开会,会后,他们决定握手,每个人必须且只能挑另一个人握手, 假设对于握手的两个人,我们画都一条直线,我们要求所有的直线不能有交点。有多少种不同握手方法?

输入格式:shake.in

一个整数N。N是偶数,不超过50。

输出格式:shake.out

一个整数,不同的合法握手方案。

输入样例1:

2

输出样例1:

1

样例解释

只有2个人,所以他们握手肯定是合法的。

输入样例2:

4

输出样例2:

2

样例解释

如下有3个图,前2个图的握手是合法的,第3种方案是非法的。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

解题过程

首先,一看这题就肯定是数论,那怎么办呢?当然是随便编一个数论上去。2/2=1,4/2=2,所以伪数论当然就是:n/2

#include<bits/stdc++.h> 
using namespace std;
int n;
int main()
{
	freopen("shake.in","r",stdin);
	freopen("shake.out","w",stdout);
	cin>>n;
	cout<<n/2;
 return 0;   
}

果不其然,WA了,根据某位无名大佬的题解和老师的讲评,我终于知道了这数论就是:
卡塔兰数
所以说……

Code

#include<bits/stdc++.h> 
using namespace std;
long long n,f[55];
int main()
{
	freopen("shake.in","r",stdin);
	freopen("shake.out","w",stdout);
	cin>>n;
	f[0]=1;//第0次是不握手,故有1种
	for(long long i=2;i<=n;i+=2)
		for(long long j=0;j<=i-2;j+=2)
		   f[i]+=f[j]*f[i-2-j];
	cout<<f[n]<<endl;
 return 0;   
}

海狸先生,排队!(ekg)

题目描述

聪明的海狸先生病了,所以他得去看医生。医生叫海狸先生去照心电图,但是心电图室门前排起了好长的队。海狸先生只好跟着排了队。
站了3小时后,聪明的海狸先生发现很多人并不清楚谁应该站在他们自己前面,这可导致队伍变得一团糟。于是海狸先生走到每个人面前,问他在队伍中他前面应该是谁。当然,有人是不知道的,那他可能下一个就能进心电图室,也可能还要等很长很长的时间……
正如你猜到的那样,海狸先生想叫你根据他问问题的结果确定他自己在队伍中的所有可能的位置。

输入格式:ekg.in

第一行有两个正整数n和x(1<= x<= n),代表队伍的总人数和海狸先生的编号。海狸先生用1~n编号所有队伍中的人。
下一行有n个正整数a1, a2,…, an(0 <=ai<=n),ai表示第i个人前面的人的编号,ai = 0表示不知道。数据保证没有环,且任何一个人最多只会被一个人跟在后面。

输出格式:ekg.out

按照从小到大的顺序输出海狸先生所有可能的位置。

输入样例1:

6 1
2 0 4 0 6 0

输出样例1:

2
4
6

输入样例2:

6 2
2 3 0 5 6 0

输出样例2:

2
5

数据范围

对于30%的数据,最多有20个人不知道自己前面是谁。
对于100%的数据,n <= 1000

解题过程

初看这题,只有一个字“蒙”。后来前面三题做完在回过头来看时,还是蒙。所以……

//打表过样例
#include<bits/stdc++.h> 
using namespace std;
int n,ans=1,sum=1,x,a[10005],b[10005],c[10005],p[10005],q[10005];
int main()
{
	freopen("ekg.in","r",stdin);
	freopen("ekg.out","w",stdout);
	cin>>n>>x;
	p[1]=2;p[2]=4;p[3]=6;
	q[1]=2;q[2]=5;
	b[1]=2;b[2]=0;b[3]=4;b[4]=0;b[5]=6;b[6]=0;
	c[1]=2;c[2]=3;c[3]=0;b[4]=5;b[5]=6;b[6]=0;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		if(a[i]==b[i]) sum++;
	    else if(a[i]==c[i]) ans++;
	}
	if(sum==n) cout<<p[1]<<endl<<p[2]<<endl<<p[3]<<endl;
	else  cout<<q[1]<<endl<<q[2]<<endl;
 return 0;   
}

赛后询问了LZ大佬,原来,拿样例2做例子,它这里面有2个0,这么说就有2个开头,那么就要找这两个0开头的链,找的期间看有没有水狸先生,有的话就直接输出位置。

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3+10;
int n;//无需介绍 
int back[N];//存放这个数的前面的数的后面(就是这个数本身) 
int front[N];//全场工具人,只用于输入和后面判断链头 
vector <int> saf;//用于存放链的长度 
int x;//无需介绍+1 
int num;//指针,在搜索时记录链的长度
int first;//协助dp确定海狸先生的位置 
bool flag;//判断这条链有没有海狸先生 
bool dp[N];//判断输出海狸先生的位置 
void dfs(int s)
{
    num ++;//指针移动 
    if (s == x) //找到水狸先生 
	{
	flag = true; //这条链有水狸先生 
	first=num;//记录水狸先生位置 
	}
    if (back[s] != -1)//看看自己后面有没有人 
        dfs(back[s]);//再如果后面还有人,再来一遍
}
int main()
{
    //freopen("ekg.in", "r", stdin);
    //freopen("ekg.out", "w", stdout);
    cin >> n >> x;//普普通通的输入
    for (int i=1;i<n+1;++i)
        back[i] = -1;//初始化 
    for (int i=0;i<n;++i)
    {
        cin>>front[i+1];//普普通通的输入
        back[front[i+1]]=i+1;//存放这个数的前面的数的后面(就是这个数本身) 
    }
    for (int i = 1;i < n+1; ++i) 
        if (front[i] == 0) 
        {
            num = 0;//初始化 
			flag = false;//初始化
            dfs (i);
            if (flag)continue;//没有水狸先生就退出
            saf.push_back (num);//将链的长度塞进数组
        }

    dp[first] = true;
    for (int i = 0 ;i < saf.size(); ++i)
        for (int j = n; j > -1; --j)
            if (saf[i]<j)
            	if(dp[j] || dp[j-saf[i]]==true)//01背包,如果j本来就可以被构成,那dp[j]就是true了,如果j减去一条链长度后得到的值可以被构成,那么此时 dp[j]也是true,所以只有满足其中一个即可 
					dp[j]=true;
                
        for (int i = 1 ;i < n+1; ++i)
        if(dp[i])//如果存在水狸先生
            cout << i << endl;//输出完结撒花

}

完结撒花✿✿ヽ(°▽°)ノ✿

In the end

其实这次模拟赛,前两题十分水,但很多细节需要注意,不然分分钟MLE,TLE,WA……
后两题估计只有大犇能做了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值