~模拟赛2~

一.人员借调

1.题目描述

B 地的领导想借调小可帮忙处理 件事情,小可处理第 件的耗时为分钟。
正常借调的过程为小可从 A 地到 B 地进行处理,处理结束之后回到 A 地。
如果小可在 B 地待连续大于等于240分钟时,A 地领导将强制把小可留在 A 地 10080分钟,结束后小可可以继续留在 A 地正常工作或者继续前往B 地帮忙处理事情。
于是,小可有了一个对策,在240分钟快到的时候就此 B 地回到 A 地,然后再去 B 地,这样的话
分钟就会重新计时。注意:从 A 地往返一次 B 地会耗时400分钟(这个时间不计算到待在 B 地的时间)。
现在小可从 A 地准备出发,需要在 B 地处理完所有事,然后回到 A 地正常的工作。
请问小可至少需要多少分钟?

2.思路

分类讨论:

        (1)卡点回,来回往返

        (2)做完所有事情再回去挨罚 摆烂了

取最小值,注意别忘加往返的400分钟

3.AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,x;
	cin>>n; 
	long long ans=400,sum=0,w=0;
	for(int i=0;i<n;i++)
	{
		cin>>x;
		ans+=x;
		sum+=x;
		if(sum>=240)
		{
			w++;
			sum=x;
		}
	}
	if(w>=1)
	{
		if(n==1)
		{
			ans+=10080;
			cout<<ans;
			return 0;
		}
		cout<<min(ans+10080,ans+w*400);
		return 0;
	}
	cout<<ans;
}

二.计算

1.题目描述

计算需要使用到三个整数m,n,k
计算条件如下(设x为满足计算条件的数字):
        (1):m\leq x\leq n
        (2): x在十进制下所有位上的数字之和为k
        请输出十进制下所有位上的数字的积最大的那个(如果有多个积相等,则输出 最小的那个)

2.思路

数据较小,long long存的下,暴力枚举70分O(n\log n)

根据sum(x)=sum(floor(x/10))+x%10可以预处理5000000以内数据O(n)

3.AC代码

#include<bits/stdc++.h>
using namespace std;
int p[10000001];
int e[10000001];
int main()
{
    int t,n,m,k,ans=0;
	cin>>t;
	p[0]=1;
    for (int i=1;i<=5000000;i++)
    {
        e[i]=e[i/10]+(i%10);
        p[i]=p[i/10]*(i%10);
    }
	p[0]=-1;
	while(t--)
	{
		ans=0; 
		cin>>n>>m>>k;
		for(int i=n;i<=m;i++)
		{
			if(e[i]==k&&p[i]>p[ans])
			{
				ans=i;
			}
		}
		cout<<ans<<" "<<p[ans]<<endl; 
	}	
	
} 

三.智能公交

1.题目描述

马路上总共有n个公交站台,编号 1~n。例如,有人想从第五个公交站台到第十个公交站台,那么公交车会先从第 个站台跑到第五个站台,然后再从第五个站台跑到第十个站台,然后再回到第 站台停下。假设相邻的两个站台的距离都是1千米,那么智能公交总共行走了\left |x-5 \right |+\left |5-10 \right |+\left |10-x \right |的距离。
现在有m个人要依次乘坐智能公交,每个人都会等待智能公交停在x站台之后在按动当前站台按钮准备乘坐公交。现在已知第i个人都是从a站台到b站台。请你计算 ,使得智能公交移动距离最短。最终输出x和最短的距离, 输出最小的一个。

2.思路

枚举每个点作为停靠站点,然后计算距离,查询最小距离。(50分)
公交车从站台a到站台b,

        (1)停靠位置x在a到b之间,那么移动距离为:\left |x-a \right |+\left |a-b \right |+\left |b-x \right |=2*\left | a-b \right |
        (2)若停靠位置在a-1,则公交车要多移动2距离。

        (3)若停靠位置在a-2,则公交车要多移动4距离。

我们可以发现,停靠位置从a-1到1,公交车              多移动的距离呈公差为2的等差数列。


我们假设分f[x]表示公交车停靠在 的总移动距离,那么给定a,b的时候,相当于将整个数组全部加 
2*\left | a-b \right |,并且将a-1到1的位置额外加一个公差为 2 的等差数列;同理,将b+1到n的位置加
一个公差为2的等差数列。
用差分做区间加法,等差数列加法

3.AC 代码

#include<bits/stdc++.h>
using namespace std;
long long x,y,a[1000001],b[1000001];
long long a1[1000001],b1[1000001];
int main()
{
	long long n,sum=0,m;
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		cin>>x>>y;
		a[x-1]+=2;
		b[y+1]+=2;
		sum+=(y-x)*2;
	}
	for(int i=n;i>=1;i--)
	{
		a[i]+=a[i+1];
		a1[i]=a1[i+1]+a[i];
	}
	long long max=1e18+9,maxp=0;
	for(int i=1;i<=n;i++)
	{
		b[i]+=b[i-1];
		b1[i]=b1[i-1]+b[i];
		if(a1[i]+b1[i]<max)
		{
			max=a1[i]+b1[i];
			maxp=i;
		}
	}
	cout<<maxp<<" "<<max+sum;
	return 0;
}

四.异或和

1.题目描述

多个集合中总共有n个数字,并且已知每个数字的大小ai和属于某个集合bi。
在一个集合中选择一个数字,收益为这个数字的大小,选择多个数字,收益为这些数字的异或和。总收益为每个集合的收益之和。注意:最多从中选择m个数字,使这些数字总收益最大。

2.思路

m=n,b=1时,在一个组织内任意选数

f[i][j]表示看到第i个数,能否亦或出j(能为1,不能为0)

for (int i = 1; i <= n; i++) 
{
    for (int j = 1; j <= 2047; j++) 
    {
        if (f[i - 1][j]) {
            f[i][j] = 1;
            f[i][j ^ a[i]] = 1;
        }
    }
}

再加入m的限制,可将f存储的内容变为第i个数,异或得到j至少需要几个数。

转移时需要min,可以用1e9表示,最后再遍历一遍,判断个数小于等于m的。

for (int i = 1; i <= n; i++)
{
    for (int j = 1; j <= 2047; j++)
    {
        if (f[i - 1][j] != 1e9)
        {
            f[i][j] = min(f[i][j], f[i - 1][j]);
            f[i][j ^ a[i]] = min(f[i - 1][j], f[i][j ^ a[i]]);
        }
    }
}

如果对于每一个集合都进行一次如上操作,就能AC。但是开成三维数组会内存超限,所以可以用二维数组+一个一位数组num[i][j]表示第i组选j个数最大的异或值。最终使用一次分组背包,就可以了

3.AC代码

#include <bits/stdc++.h>
using namespace std;
int n, m, dp[2005][2050],s[2005][2005], f[2050], b[2005];
int a[2005][2005],vl=0;
int k[1001];
int main()
{
	cin>>n>>m; 
	int x,y; 
	for(int i=1;i<=n;i++)
	{
		cin>>x>>y;
		a[y][k[y]++]=x;
		b[y]++;
	}
	for (int i=1;i<=n;i++) 
	{
		for (int j=1;j<2048;j++)
		{
			dp[i][j]=1e9+9;
		}
	}
	for (int zu=1;zu<=2000;zu++)
	{
	
		if (b[zu]!=0)
		{
			dp[1][a[zu][0]]=1;
		}	  
		for (int i=2;i<=b[zu];i++)
		{
			dp[i][a[zu][i-1]]=1; 
			for (int j=1;j<=2047;j++)
			{
				if(dp[i-1][j]!=1e9)
				{
					
					dp[i][j] = min(dp[i][j], dp[i-1][j]);
					dp[i][j^a[zu][i-1]]=min(dp[i-1][j]+1,dp[i][j^a[zu][i-1]]);
				}
			}
		}
		for (int j=1;j<=2047;j++)
		{
			if(dp[b[zu]][j]!=1e9)
				s[zu][dp[b[zu]][j]]=j;
				
		}
		for (int i=1;i<=b[zu];i++)
		{
			for(int j=1;j<=2047;j++)
			{
				dp[i][j]=1e9;
			}
		}
	}
	for(int i=1;i<=2000;i++)
	{
		for (int j=m;j>=1;j--)
		{
			for(int k=1;k<=b[i];k++)
			{ 
				if(j>=k)
				{
					f[j]=max(f[j],f[j-k]+s[i][k]);
				}
			}
		}
	}
	cout<<f[m];
	return 0;
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~THE END~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值