AcWing 第11场周赛题解

计算abc

首先 \(0<=a<=b<=c\)
会随机给出
\(a+b,a+c,b+c,a+b+c\)的值

因为\(a,b,c\)都为正整数,所以\(a+b+c\)一定为最大值
然后用\(a+b+c\)逐个减去即可(注意\(a,b,c\)大小

  • code
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int x[5];
int main(){
	for(int i=1;i<=4;++i) cin>>x[i];
	sort(x+1,x+5);
	for(int i=3;i>=1;--i) cout<<x[4]-x[i]<<" ";
	return 0;
} 

凑平方

根据题意模拟,不难想到暴力枚举,枚举出每一个由原数删了后的数
再看数据范围,数的长度最多十位,所以每个数最多枚举2^10次,每次枚举最多10次,\(t<=10\)组数据
n为数的长度
所以时间复杂度\(O(2^n*n*t)\)

  • 具体实现
    (dfs,bfs都可以)
    这里有一种巧妙,易实现的方法

利用二进制
举个例子
原数\(2081\)
$(1010)_2 $ 1就代表不删相应位置数,0代表删了相应位置的数
按照上述规则,删了后的数为 28
\((0111)_2\) 删了后的数字为 081 ,但这里你会发现有前导0,但其实具体实现的时候不会有这个问题,因为我们是一位一位的转化成整型,巧妙的避免了前导0的问题
既然是找最少次删除操作
那枚举顺序\(0-2^n\)

  • code
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>

using namespace std;
string s;
int t;
int stlen(int x){//求x的位数
    int ans=0;
    for(;x;++ans) x/=10;return ans;
}
int main(){
    cin>>t;
    while(t--){
        cin>>s;
        int len=s.length();
        int ans=100;
        for(int i=0;i<(1<<len);++i){
            int x=0;
            for(int j=0;j<len;++j)
                if(i>>j & 1)
                    x=x*10+s[j]-'0';
            int sq=sqrt(x);
            if(x && sq*sq==x) ans=min(ans,len-stlen(x));
        }
        if(ans!=100) cout<<ans<<endl;
        else cout<<"-1"<<endl;
    }
}

最大化最短路

突破口 :必须选出两个特殊点
那么考虑三种情况
设两个特殊点分别为\(x,y\)

  • 选了\(x,y\)但不经过任意\(x,y\)

  • 经过两个\(x,y\)最短路径是从\(1——>n\)

  • 经过两个\(x,y\)最短路径是从\(n——>1\)

\(dis1[],dis2[]\)分别为1到各个点的最短距离,n到各个点的最短距离
三种情况分别能得出的答案即为
1.\(dis1[n]\) (不经过任何点即为最短路
2.\(dis1[x]+1+dis2[y]\) (1——>x+x——>y+y——n
3.\(dis2[x]+1+dis1[y]\) (n——>x+x——>y+y——1

求出后两种情况的最大值之后
在三者情况种取最小值(为什么不取较大两个值,因为较大两个虽然构成最短路,但在三种情况一起讨论的时候,并不较大的两个值并不构成最短路
也就是说,添加一条边之后很有可能最短距离会减小)

第一情况跑一下最短路就可
第二种和第三种情况,首先要预处理
\(dis1[]和dis2[]\),跑一遍最短路即可

最终求的是1到n最短距离尽可能大

所以现在的目标就是求出2,3情况的最大值,取其中得最小值(
如果暴力求话直接GG
假设\((i,j)\)为一对且\(i!=j\),而且i,j∈特殊点
任取一对,则二三情况得到的答案
\(max(min(dis1[i]+1+dis2[j],dis1[j]+dis[i]+1))\)

当dis1[i]+1+dis2[j]<=dis1[j]+dis[i]+1时
即dis1[i]-dis2[i]<=dis[j]-dis2[j];

将数对根据差值排序
此时
当数对后一个为x时
取max(dis1[1~x-1])+dis2[x])
x从1~k

取出的最大值,最后与dis1[n]取最小值即可

  • code
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<iostream>

using namespace std;
int n,m,k;const int maxn=2e5+10;
int head[maxn*2];
int p[maxn];
int cnt=0;
int dis1[maxn],dis2[maxn];
struct node{
    int v,next;
}e[maxn*2];
void add(int u,int v){
    e[++cnt].v=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}
bool cmp(int a,int b){
    return dis1[a]-dis2[a]<dis1[b]-dis2[b];
}
void dj(int st,int dis[]){
    bool vl[maxn];
    memset(vl,0,sizeof(vl));
    memset(dis,0x3f3f3f3f,maxn*4);
    dis[st]=0;
    priority_queue<pair<int,int>  >q;
    q.push(make_pair(-dis[1],st));
    while(!q.empty()){
        int u=q.top().second;q.pop();
        if(vl[u]) continue;
        vl[u]=1;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].v;
            if(dis[v]>dis[u]+1){
                dis[v]=dis[u]+1;
                q.push(make_pair(-dis[v],v));
            }
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&k);memset(head,0,sizeof(head));
    for(int i=1;i<=k;++i) scanf("%d",&p[i]);
    for(int i=1;i<=m;++i){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dj(1,dis1);
    dj(n,dis2);
    sort(p+1,p+1+k,cmp);
    int res=0,x=dis1[p[1]];
    for(int i=2;i<=k;++i){
        int t=p[i];
        res=max(res,dis2[t]+x+1);
        x=max(x,dis1[t]);
    }
    printf("%d",min(res,dis1[n]));return 0;
}
posted @ 2021-08-09 23:02  归游  阅读(73)  评论(0编辑  收藏  举报