AtCoder Beginner Contest 184

前两题略~~

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值