一.人员借调
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):
(2): x在十进制下所有位上的数字之和为k
请输出十进制下所有位上的数字的积最大的那个(如果有多个积相等,则输出 最小的那个)
2.思路
数据较小,long long存的下,暴力枚举70分
根据sum(x)=sum(floor(x/10))+x%10可以预处理5000000以内数据
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千米,那么智能公交总共行走了的距离。
现在有m个人要依次乘坐智能公交,每个人都会等待智能公交停在x站台之后在按动当前站台按钮准备乘坐公交。现在已知第i个人都是从a站台到b站台。请你计算 ,使得智能公交移动距离最短。最终输出x和最短的距离, 输出最小的一个。
2.思路
枚举每个点作为停靠站点,然后计算距离,查询最小距离。(50分)
公交车从站台a到站台b,
(1)停靠位置x在a到b之间,那么移动距离为:
(2)若停靠位置在a-1,则公交车要多移动2距离。
(3)若停靠位置在a-2,则公交车要多移动4距离。
我们可以发现,停靠位置从a-1到1,公交车 多移动的距离呈公差为2的等差数列。
我们假设分f[x]表示公交车停靠在 的总移动距离,那么给定a,b的时候,相当于将整个数组全部加 ,并且将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~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~