北京师范大学第十六届程序设计竞赛决赛-重现赛 - (D,E,F,G)

比赛链接:https://www.nowcoder.com/acm/contest/117#question

D雷电爆裂之力

题意:题意:有三个长度分别为 n, m, k 的元素值严格递增的整数数组a, b, c。求 min(abs(ai − bj) + abs(bj − cp)) + 3 的值,其中1 ≤ i ≤ n, 1 ≤ j ≤ m, 1 ≤ p ≤ k。

解析:一个比较简单的思路是枚举 bj,通过双指针的方法,找到距离 bj 最近的 ai 和 cp,更新答案。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;

int n,m,k;
ll a[N],b[N],c[N];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0;i<n;i++) scanf("%lld",&a[i]);
        for(int i=0;i<m;i++) scanf("%lld",&b[i]);
        for(int i=0;i<k;i++) scanf("%lld",&c[i]);

        ll ans=1e12;
        int pos1=0,pos2=0;
        for(int i=0;i<m;i++)
        {
            ll dis1=1e12,dis2=1e12;
            while(pos1<n&&a[pos1]<b[i]) pos1++;
            if(pos1>0) dis1=min(dis1,b[i]-a[pos1-1]);
            if(pos1<n) dis1=min(dis1,a[pos1]-b[i]);

            while(pos2<k&&c[pos2]<b[i]) pos2++;
            if(pos2>0) dis2=min(dis2,b[i]-c[pos2-1]);
            if(pos2<k) dis2=min(dis2,c[pos2]-b[i]);

            ans=min(ans,dis1+dis2);
        }
        printf("%lld\n",ans+3);
    }
    return 0;
}
E可以来拯救吗

题意:给出长度为n的序列:a1,a2...an,求所有长为k的子序列的和的平方的异或和

解析:题目有如下说明

 

保证,也就是说,需要考虑的子序列不超过100000个。

那么如果n比较大的话,k要不就比较小,要不就接近n,所以可以用dfs来做,对于k<=n/2时,直接深搜长度为k的子序列,对于k>n/2时,深搜长度为n-k的子序列,然后用序列总和sum减之,来得到真正想要的长度为k的和。代码写的繁琐,可以在简化一下

代码:

#pragma comment(linker,"/STACK:102400000,102400000")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define M 100005
ll n,k,a[M];
ll ans,sum;
void dfs1(ll i,ll len,ll res)
{
    if(len>k) return;
    for(ll j=i+1;j<=n;j++)
    //for(int j=i+1;j<=n;j++)
    {
        res+=a[j];
        if(len==k)
        {
            //cout<<"res="<<res*res<<endl;
            ans=ans^(res*res);
        }else{
            dfs1(j,len+1,res);
        }
        res-=a[j];
    }
}
void dfs2(ll i,ll len,ll res)
{
    if(len>k) return;
    for(ll j=i+1;j<=n;j++)
    //for(int j=i+1;j<=n;j++)
    {
        res+=a[j];
        if(len==k)
        {
            //cout<<"res="<<res*res<<endl;
            ll tmp=sum-res;
            ans=ans^(tmp*tmp);
        }else{
            dfs2(j,len+1,res);
        }
        res-=a[j];
    }
}
int main()
{
    //cout<<(1^4^9^16)<<endl;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&k);
        sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            sum+=a[i];
        }
        ans=0;
        if(k<=n/2)
        {
            if(k==1)
            {
                for(int i=1;i<=n;i++)
                {
                    ans=ans^(a[i]*a[i]);
                }
            }else
            {
                for(int i=1;i<=n-k+1;i++)
                {
                    dfs1(i,2,a[i]);
                }
            }
        }
        else
        {
            k=n-k;
            if(k==0)
            {
                ans=sum*sum;
            }else if(k==1)
            {
                for(int i=1;i<=n;i++)
                {
                    ans=ans^((sum-a[i])*(sum-a[i]));
                }
            }
            else
            {
                for(int i=1;i<=n-k+1;i++)
                {
                    dfs2(i,2,a[i]);
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

F汤圆防漏理论
题意::有一个 n 个点 m 条边的无向图,边有边权,每次删一个点及其当前连接的所有边,花费是此次删点所删掉边的边权和,要求最小化每次花费的最大值。

解析:直接贪心。记每个点点权为其当前连接的所有边的边权和,每次取出最小的点,然后删掉。用 set 动态维护这个过程。时间复杂度 O(n log n)。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define M 100005
ll sum[M];  //每个节点的粘度值
bool vis[M];//对于已经删除的节点i,vis[i]=true
vector< pair<ll,ll> >G[M];//G[i]是一个pair数组记录与i点相邻点的情况(相邻点的编号和两点间权值)
set< pair<ll,ll> >st;     //set中元素自动升序排,set中放的是每个点的粘度值总和及其编号
int main()
{
    int T,n,m;
    ll s,t,v;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<M;i++)
        {
            G[i].clear();
            sum[i]=0;
            vis[i]=false;
        }
        st.clear();

        while(m--)
        {
            scanf("%lld%lld%lld",&s,&t,&v);
            G[s].push_back( pair<ll,ll>(t, v) );
            G[t].push_back( pair<ll,ll>(s, v) );
            sum[s]+=v;sum[t]+=v;
        }
        for(int i=1;i<=n;i++)
        {
            st.insert(pair<ll,ll>(sum[i],i));
        }
        ll ans=0;
        while(!st.empty()) //set集合中是还未删除的点
        {
            set< pair<ll,ll> >::iterator it=st.begin();//it指向粘度值最小的点
            pair<ll,ll> now=*it;
            st.erase(it);            //删除此点
            ans=max(ans,now.first);  //在删除点的过程中出现的最大的粘度值就是答案
            ll index=now.second;     //index是此点的编号
            vis[index]=true;
            for(int i=0;i<G[index].size();i++)//枚举index点的相邻节点,删除两者之间的边(对index的相邻节点来说就是删除与点index之间的粘度值)
            {
                pair<ll,ll> next=G[index][i];//next是相邻点的信息
                if(vis[next.first]) continue;
                ll u=next.first;            //u是相邻点的编号
                it=st.lower_bound(pair<ll,ll>(sum[u],u));//在set集合中找到点u
                //更新点u的信息
                st.erase(it);
                sum[u]-=next.second;
                st.insert(pair<ll,ll>(sum[u],u));
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
G命名规范问题


题意:给一些字符串,将符合 (题中描述的) 驼峰命名法规范的变量名转换为下划线命名法。不符合的原样输出。

规范的规则如下:

1.每个变量名由至少2个单词拼接构成,且每个单词长度至少为2;

2.每个单词的首字母必须大写,其他位置必须小写(除了变量名的第一个单词允许全部小写外)。

正则表达式代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    regex reg("\\b[A-Za-z][a-z]+([A-Z][a-z]+)+\\b");
    regex cap("[A-Z]");
    int T;
    cin>>T;
    while(T--)
    {
        string now;
        cin>>now;
        if(regex_match(now,reg))
        {
            now=regex_replace(now,cap,"_$0");
            transform(now.begin(),now.end(),now.begin(),::tolower);
        }
        if(now[0]=='_') now.erase(0,1);
        cout<<now<<endl;
    }
}

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define M 200005
char str[M];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",str);
        int len=strlen(str);
 
        int flag=0;
        for(int i=0;i<len;i++)
        {
            if(str[i]>='A'&&str[i]<='Z'){flag++;}
        }
        if(flag==0||(flag==1&&str[0]>='A'&&str[0]<='Z'))
        {
            printf("%s\n",str);
            continue;
        }
        flag=1;
        int num=0;
        for(int i=0;i<len;i++)
        {
            if(str[i]>='A'&&str[i]<='Z')
            {
                if(i!=0&&num<2) {flag=0;break;}
                num=1;
            }else{
                num++;
            }
        }
        if(num<2) {flag=0;}
        if(flag==0)
        {
            printf("%s\n",str);
            continue;
        }
 
        string ans="";
        for(int i=0;i<len;i++)
        {
            if(str[i]>='A'&&str[i]<='Z')
            {
                if(i!=0) ans+="_";
                ans+='a'+(str[i]-'A');
            }else{
                ans+=str[i];
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值