牛客寒假算法训练第五场

A题:https://ac.nowcoder.com/acm/contest/331/A

题解:明显木棍的另一端到不了 的点肯定位于0到| L1-L2 |或者大于(L1+L2)的这两部分。易错可能就是精度吧

#include <bits/stdc++.h>
using namespace std;
#define eps 1e-6
double dis(double x,double y)
{
    double ans=x*x+y*y;
    return sqrt(ans);
}
int main()
{
    double l1,l2,x,y;
    int T;
    scanf("%lf %lf",&l1,&l2);
    scanf("%d",&T);
    while(T--){
        scanf("%lf %lf",&x,&y);
        double cnt=dis(x,y);
        if(cnt-(l1+l2)>eps){
            printf("%lf\n",cnt-(l1+l2));
        }
        else if(fabs(l1-l2)-cnt>eps){
            printf("%lf\n",fabs(l1-l2)-cnt);
        }
        else printf("0\n");
    }
    return 0;
}

D题:https://ac.nowcoder.com/acm/contest/331/D

题解:虽然N很大,其实主要看K。图的构建只要把起点和终点加入,再加上额外连的边(传送法阵)。因为两点之间是单向传递,图为有向图。求出图中各点到起点的最短距离就好。

代码:

#include <bits/stdc++.h>
using namespace std;
#define maxx 1006
typedef long long ll;
ll N,K;
ll dist[maxx];
vector <ll> p;
ll x[maxx],y[maxx];
int main()
{
    scanf("%lld %lld",&N,&K);
    p.push_back(1);
    p.push_back(N);
    for(int i=1;i<=K;i++){
        scanf("%lld %lld",&x[i],&y[i]);
        if(x[i]>y[i]){
            swap(x[i],y[i]);
        }
        p.push_back(x[i]);
        p.push_back(y[i]);
    }
    sort(p.begin(),p.end());
    memset(dist,0x3f,sizeof dist);
    p.erase(unique(p.begin(),p.end()),p.end());
    dist[0]=0;
    for(int i=0;i<p.size();i++){
        for (int j=i+1;j<p.size();j++){
            for (int k=1;k<=K;k++){
                if(p[i]==x[k]&&p[j]==y[k]){
                    dist[j]=min(dist[i]+1,dist[j]);
                }
            }
            dist[j]=min(dist[i]+__builtin_popcount(p[j]-p[i]),dist[j]);//如果该两点之间没有传送门,则该两点之间的距离可以根据题意计算出。
        }
    }
    printf("%lld",dist[p.size()-1]);
    return 0;
}

E题:平面上有一个圆,圆环上按顺时针顺序分布着从1到n,一共n个点。

现在无聊的小希开始按某种顺序对其在圆内两两连线,小希尽量避免让两条线碰撞,可是有的时候,这显然避免不了。

现在你知道小希划线的顺序是什么,请你判断小希在最优情况下,什么时候会被迫使得线相交,输出最早的时刻(是第几条线)

题解:线段树维护区间最值,每次添加的线段,将左端点下标赋值给右端点,右端点下标赋值给左端点,每次添加线段做修改前判断这个区间的最小值是否小于当前区间的左端点或者最大值是否大于当前区间右端点,如果是,则有线段相交。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
#define inf 0x3f3f3f3f
#define maxn 100010
ll sum[maxn<<2];//保存区间和
ll sum1[maxn<<2];//用于输入保存数据
void pushup(int x)
{
    sum[x]=min(sum[x<<1],sum[x<<1|1]);
}
void pushup1(int x)
{
    sum1[x]=max(sum1[x<<1],sum1[x<<1|1]);
}
void buildtree(int l,int r,int rt)
{
        sum[rt]=inf;//叶子节点的插入
        sum1[rt]=0;
    if(l==r){
        //sum[rt]=inf;//叶子节点的插入
        //sum1[rt]=0;
        return ;
    }
    int m=(l+r)>>1;
    buildtree(l,m,rt<<1);//建立左子树
    buildtree(m+1,r,rt<<1|1);//右子树
    //pushup(rt);//维护各区间
}
void singlemodify(int X,int L,int l,int r,int rt)
{
    if(l==r){
        sum[rt]=X;
        return  ;
    }
    int m=(l+r)>>1;
    if(m>=L){
        singlemodify(X,L,l,m,rt<<1);//查询左边
    }
    else singlemodify(X,L,m+1,r,rt<<1|1);//查询右边
    pushup(rt);//更新区间
}
void singlemodify1(int X,int L,int l,int r,int rt)
{
    if(l==r){
        sum1[rt]=X;
        return  ;
    }
    int m=(l+r)>>1;
    if(m>=L){
        singlemodify1(X,L,l,m,rt<<1);//查询左边
    }
    else singlemodify1(X,L,m+1,r,rt<<1|1);//查询右边
    pushup1(rt);//更新区间
}
int query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R){
        return sum[rt];
    }
    int m=(l+r)>>1;
    //LL ans=-1;
    if(m>=R) return query(L,R,l,m,rt<<1);
    if(m<L) return query(L,R,m+1,r,rt<<1|1);
    else return min(query(L,R,l,m,rt<<1),query(L,R,m+1,r,rt<<1|1));
}
int query1(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R){
        return sum1[rt];
    }
    int m=(l+r)>>1;
    //LL ans=0;
    if(m>=R) return query1(L,R,l,m,rt<<1);
    if(m<L) return query1(L,R,m+1,r,rt<<1|1);
    else return max(query1(L,R,l,m,rt<<1),query1(L,R,m+1,r,rt<<1|1));
}
int T;
int N,M;
int main()
{
    int x,y;
    scanf("%d",&T);
    while (T--){
        int round=0;
        scanf("%d %d",&N,&M);
        buildtree(1,N,1);
        for(int i=1;i<=M;i++){
            scanf("%d %d",&x,&y);
            if(x>y){
                swap(x,y);
            }
            //printf("%d %d\n",query1(x,y,1,N,1),query(x,y,1,N,1));
            if(round==0&&(query1(x,y,1,N,1)>y||query(x,y,1,N,1)<x)){
                round=i;
            }
            singlemodify(x,y,1,N,1);
            singlemodify1(y,x,1,N,1);
        }
        if(round==0){
            printf("-1\n");
        }
        else{
            printf("%d\n",round);
        }
    }
    return 0;
}

H题:小希在家里做着作业,外面飘起了斗大的雪花,很冷!

小希把接下来连续的要做作业的时间分成n个单位,每个单位时间内小希都会受到a_i的寒冷值侵袭,她可以选择在任何一些时间站起来蹦蹦跳跳,以使得这个单位的寒冷值不侵袭她。

小希最大能承受的寒冷程度是K,但是她想选择尽可能多的时间做作业,请你帮帮她!

小希受到的寒冷程度即为不蹦蹦跳跳的时间的寒冷值总和

题解:动态规划题。dp[i][j]表示i~n段时间内跳j次所承受的最小寒冷值。主要难在方案的输出。要选择字典序最小,即应尽量把学习的时间段选在前面。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
int n;
ll K,a[5050];
ll dp[5010][5010];
char s[5050];
int main()
{
    scanf("%d %lld",&n,&K);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        s[i]='1';
    }
    memset(dp,inf,sizeof dp);
    dp[n+1][0]=0;
    for(int i=n;i>=1;i--){
        dp[i][0]=0;
        for(int j=1;j<=n-i+1;j++){
            if(dp[i+1][j-1]+a[i]>dp[i+1][j]){
                dp[i][j]=dp[i+1][j];
            }
            else{
                dp[i][j]=dp[i+1][j-1]+a[i];
            }
        }
    }
    int ans=0;
    for(int i=n;i>=1;i--){
        if(dp[1][i]<=K){
            ans=i;
            break;
        }
    }
    int t=1;
    int j=ans;
    ll tk=K;
    for(int i=1;i<=n;i++){
        if(dp[i+1][j-1]<=tk-a[i]){
            s[i]='0';
            j--;
            tk-=a[i];
        }
    }
    printf("%d\n",ans);
    printf("%s\n",s+1);
    return 0;
}

I题:https://ac.nowcoder.com/acm/contest/331/I

题解:按照题意模拟一下即可,dfs做模拟光线。

代码:

//1: up,2: down,3: left,4: right
#include <bits/stdc++.h>
using namespace std;
int N,M;
char maps[510][510];
void dfs(int x,int y,int status)
{
    if((x<0&&status==1)){
        printf("-1\n");return ;
    }
    if((y<0&&status==3)||(y>=M&&status==4)){
        printf("-1\n");return ;
    }
    if(x==N&&status==2){
        printf("%d\n",y+1);
        return ;
    }
    if(maps[x][y]=='\\'){
           if(status==1){
               dfs(x,y-1,3);
           }
           else if(status==2){
               dfs(x,y+1,4);
           }
            else if(status==3){
                dfs(x-1,y,1);
            }
            else if(status==4){
                dfs(x+1,y,2);
            }
       }
        else if(maps[x][y]=='/'){
            if(status==1){
                dfs(x,y+1,4);
            }
            else if(status==2){
                dfs(x,y-1,3);
            }
            else if(status==3){
                dfs(x+1,y,2);
            }
            else if(status==4){
                dfs(x-1,y,1);
            }
        }
        else{
            if(status==1){
                dfs(x-1,y,1);
            }
            if(status==2){
                dfs(x+1,y,2);
            }
            if(status==3){
                dfs(x,y-1,3);
            }
            if(status==4){
                dfs(x,y+1,4);
            }
        }
}
int main()
{
    scanf("%d %d",&N,&M);
    for(int i=0;i<N;i++){
        scanf("%s",maps[i]);
    }
    for(int i=0;i<M;i++){
        dfs(0,i,2);
    }
    return 0;
}

J题:https://ac.nowcoder.com/acm/contest/331/J

题解:官方题解是考虑每一位只有是(0,0) (1,0) (0,1) 这三种情况时,才是符合条件的。有M位,由乘法原理可得答案即为3^{M}

当时自己的做法是:先确定有i位是1,其余M-i位的状态就有2^{M-i}种,即答案为\sum_{i=0}^{i=M}\binom{i}{M}*2^{M-i}。感觉是乱搞得到的......

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod 998244353
const int x=300005;
ll fac[x]={1,1},inv[x]={1,1},f[x]={1,1};
ll C(ll a,ll b){
    if(b>a)return 0;
    return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
void init(){
    for(int i=2;i<x;i++){
        fac[i]=fac[i-1]*i%mod;
        f[i]=(mod-mod/i)*f[mod%i]%mod;
        inv[i]=inv[i-1]*f[i]%mod;
    }
}
ll binarypow(ll a,ll b,ll m) {
    ll ans=1;
    while(b>0){
        if(b&1){
            ans=ans*a%m;
        }
        a=a*a%m;
        b>>=1;
    }
    return ans;
}
ll M;
int main()
{
    ll ans=0;
    init();
    scanf("%lld",&M);
    if(M==0){
        printf("1\n");
    }
    else {
        for(int i=0;i<=M;i++){
            ans=ans+(1ll*(C(M,i)%mod)*(binarypow(2,M-i,mod)%mod)%mod);
            ans=ans%mod;
        }
        printf("%lld\n",ans%mod);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值