前两题略~~
C
题意
给你一个点(a,b),问你通过几步可以移动至指定点(c,d);
移动方式有三种:
a+b=c+d;
a-b=c-d;
|a-c|+|b-d|<=3;
解析
①1步到
直接判断即可。
②2 步到
定义与终点距离不大于3的集合为T,与起点距离不超过3 的集合为S。
(1)如果横坐标值差与纵坐标之差的奇偶性相同;
(2)对于S中的一个点可以一步走到终点;
(3)对于T中的一个点可以一步走到起点;
(4)S与T的交集非空。
③3 步到
不满足①②,就是③。
具体代码如下
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=1e5+10;
int main()
{
ll a,b;
ll c,d;
cin>>a>>b>>c>>d;
if(a==c&&b==d)
{
cout<<0<<endl;
return 0;
}
int k=abs(a-c);
int t=abs(b-d);
int sum=0;
if(a+b==c+d||a-b==c-d||abs(a-c)+abs(b-d)<=3)//能否一步走到
sum=1;
else if(abs(a-c)+abs(b-d)<=6||(a+b)%2==(c+d)%2)//横纵坐标的差奇偶性是否相同,或者是否在原点距离为6的范围内
{
sum=2;
}
else
{
int flag = 0;
for(int i=a-3; i<= a+3; i++)
for(int j=b-3; j<=b+3; j++)
{
if(abs(i-a)+abs(j-b) <= 3&&i+j==c+d||i-j==c-d)//可否走一步然后通过对角线走到目标点
flag = 1;
}
if(flag)
sum=2;
else
sum=3;//最多三步
}
cout<<sum<<endl;
return 0;
}
D
题目
有个箱子中有三种硬币个A,B,C个,取出一个硬币就将放回两枚相同的硬币,当一种硬币的个数达到100时结束,问现分别有a,b,c个硬币取硬币次数的数学期望为多少
解析
假设现在有 97 98 98 枚硬币,则下一步可能的结果为
1:98,98,98
2:97,99,98
2:97,98,99
则该情况的数学期望为:
(1情况的数学期望+1)(97/(98+97+98))+(2情况的数学期望+1)(98/(98+97+98)(3情况的数学期望+1)(98/(98+97+98);
由此可以推出本题的解:
dp[i][j][k]+=(dp[i+1][j][k]+1)*i/(i+j+k)
dp[i][j][k]+=(dp[i][j+1][k]+1)*j/(i+j+k)
dp[i][j][k]+=(dp[i][j][k+1]+1)*k/(i+j+k)
AC代码为:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=1e5+10;
double dp[105][105][105];
int main()
{
dp[99][99][99]=1;//当i,j,k为99时数学期望为1
for(int i=99;i>=0;i--)
{
for(int j=99;j>=0;j--)
{
for(int k=99;k>=0;k--)
{
if (i == 99 && j == 99 && k == 99)
continue;
dp[i][j][k]+=(dp[i+1][j][k]+1)*i/(j+k+i);
dp[i][j][k]+=(dp[i][j+1][k]+1)*j/(j+k+i);
dp[i][j][k]+=(dp[i][j][k+1]+1)*k/(j+k+i);
}
}
}
int n,m,k;
cin>>n>>m>>k;
printf("%.9lf",dp[n][m][k]);
return 0;
}
E
题意
现在有一个图,你将从S走到G,#为不可以走的格子,如果走到的各种上是字母a~z,相同的字母,可以一步直接走到。
问,走到G最短需要几步,如果走不到,则输出-1
解析
本题就是bfs上进行一个判断,如果走到了一个字母,则将走到其他所有的字母距离改为走到当前字母的距离+1,并且入队(因为无论如何到其他字母的距离最短就是这个),其余和正常的bfs一模一样
AC代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=2020;
typedef pair<int,int>PII;
int dis[N][N];
char ma[N][N];
int vis[N][N];
int dx[4]= {0,0,1,-1};
int dy[4]= {1,-1,0,0};
int sx,sy,gx,gy;
int path[50];//判断这种字母是否已经走过了
map<int,vector<PII> >g;//存相同字母的个数和坐标
int main()
{
int h,w;
cin>>h>>w;
for(int i=0; i<h; i++)
{
for(int j=0; j<w; j++)
{
cin>>ma[i][j];
if(ma[i][j]=='S')
{
sx=i;
sy=j;
}
if(ma[i][j]=='G')
{
gx=i;
gy=j;
}
else if(ma[i][j]>='a'&&ma[i][j]<='z')
{
g[ma[i][j]-'a'].push_back({i,j});
}
}
}
queue<PII> q;
q.push({sx,sy});
vis[sx][sy]=1;
dis[sx][sy]=0;
while(!q.empty())
{
PII k=q.front();
q.pop();
int x=k.first;
int y=k.second;
if(ma[x][y]=='G')//走到了目标点
{
cout<<dis[x][y]<<endl;
return 0;
}
for(int i=0; i<4; i++)
{
int nx=x+dx[i];
int ny=y+dy[i];
if(nx>=0&&nx<h&&ny>=0&&ny<w&&!vis[nx][ny]&&ma[nx][ny]!='#')//是否可以走
{
dis[nx][ny]=dis[x][y]+1;
vis[nx][ny]=1;
q.push({nx,ny});
}
}
if(ma[x][y]>='a'&&ma[x][y]<='z'&&!path[ma[x][y]-'a'])//当前走到了一个字母并且字母是第一次被访问
{
path[ma[x][y]-'a'] = 1;//标记本种字母被访问过了
for(int i=0; i<g[ma[x][y]-'a'].size(); i++)
{
int nx = g[ma[x][y]-'a'][i].first;
int ny = g[ma[x][y]-'a'][i].second;
if(nx>=0&&nx<h&&ny>=0&&ny<w&& !(nx==x&&ny==y) &&ma[nx][ny]!='#' &&!vis[nx][ny])//判断是否在图内
{
vis[nx][ny] = 1;
dis[nx][ny] = dis[x][y]+1;
q.push({nx,ny});//入队
}
}
}
}
cout<<-1<<endl;
return 0;
}
F
题意
有N个问题,T的时间,解决第i个问题需要耗费Ai的时间。问他最多花费多少时间来解决问题(时间不能超过T)
解析
meet in the middle的经典例题题目
因为T的范围过大,所以不能01背包,但考虑到N的范围不大(《=40),所以我们考虑meet in the middle,
将N个问题分为前后两个部分,分别进行二进制枚举(2^20大约为1e6)复杂度符合,
最后二分查找最大的耗时即可。具体细节看代码
AC代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=1e5+10;
ll a[1<<21],b[1<<21],q[50];
int main()
{
ll n,t;
cin>>n>>t;
for(int i=0; i<n; i++)
{
cin>>q[i];
}
int k=(n+1)/2;//分为两部分
int y=n-k;
int tot=0;
int num=0;
for(int i=0; i<(1<<k); i++)//对第一部分进行二进制枚举
{
ll sum=0;
for(int j=0; j<k; j++)
{
if(i&(1<<j))
{
sum+=q[j];
}
}
if(sum<=t)
a[tot++]=sum;
}
for(int i=0; i<(1<<y); i++)//对第二部分进行二进制枚举
{
ll sum=0;
for(int j=0; j<y; j++)
{
if(i&(1<<j))
{
sum+=q[j+k];
}
}
if(sum<=t)
b[num++]=sum;
}
sort(b,b+num);//对第二部分进行排序,为接下来二分做准备
ll ans=0;
for(int i=0; i<tot; i++)
{
ll sum=a[i];
sum+=b[upper_bound(b,b+num,t-a[i])-b-1];//二分查找
ans=max(ans,sum);//取最大值
}
cout<<ans<<endl;
return 0;
}