AtCoder Beginner Contest 374

代码中已去除冗余

A Takahashi san 2

直接判断末尾字符串是否为"san"即可,时间复杂度O(1)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        System.out.println(sc.next().endsWith("san")?"Yes":"No");
    }
}

B Unvarnished Report

遍历字符串找到第一个不同的字符,时间复杂度O(len(t)+len(s))

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        String s=sc.next(),t=sc.next();
        if(s.equals(t)){
            System.out.println("0");
        }
        else{
            for(int i=0;;i++){
                if(i>=Math.min(s.length(),t.length())||s.charAt(i)!=t.charAt(i)){
                    System.out.println(i+1);
                    break;
                }
            }
        }
    }
}

C Separated Lunch

根据数据范围,可以利用状态压缩来代表其中一个组选了什么,算出总数,进而知道剩下的总数,取所有情况的最小值即可,时间复杂度O(n*2^n)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),k[]=new int[n],ans=(int)2e9,sum=0;
        for(int i=0;i<n;i++){
            k[i]=sc.nextInt();
            sum+=k[i];
        }
        for(int i=0;i<(1<<n);i++){
            int cur=0;
            for(int j=0;j<n;j++){
                cur+=k[j]*(i>>j&1);
            }
            ans=Math.min(ans,Math.max(cur,sum-cur));
        }
        System.out.println(ans);
    }
}

D Laser Marking

本题的距离是欧几里得距离,根据数据范围,枚举已完成的状态以及完成时所在的线段的编号和那一端,再枚举上一次完成的线段,即可完成动态规划,时间复杂度O(n^2*2^n)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),s=sc.nextInt(),t=sc.nextInt(),p[][][]=new int[n][2][2];
        double dis1[]=new double[n],dis2[][][][]=new double[n][n][2][2],dis3[][][]=new double[1<<n][n][2],ans=1e20;
        for(int i=0;i<n;i++){
            for(int j=0;j<2;j++){
                for(int k=0;k<2;k++){
                    p[i][j][k]=sc.nextInt();
                }
            }
            dis1[i]=getDis(p[i][0],p[i][1])/t;
            for(int j=0;j<n;j++){
                for(int k=0;k<2;k++){
                    for(int w=0;w<2;w++){
                        dis2[i][j][k][w]=dis2[j][i][w][k]=getDis(p[i][k],p[j][w])/s;
                    }
                }
            }
        }
        for(int i=1,bit=0;i<(1<<n);i++){
            if(Integer.bitCount(i)==1){
                for(int j=0;j<2;j++){
                    dis3[i][bit][j]=getDis(new int[2],p[bit][j^1])/s+dis1[bit];
                }
                bit++;
            }
            else{
                for(int j=0;j<n;j++){
                    if((i>>j&1)==1){
                        Arrays.fill(dis3[i][j],1e20);
                        int mask=i^(1<<j);
                        for(int k=0;k<n;k++){
                            if((mask>>k&1)==1){
                                for(int u=0;u<2;u++){
                                    for(int v=0;v<2;v++){
                                        dis3[i][j][u]=Math.min(dis3[i][j][u],dis3[mask][k][v]+dis2[j][k][u^1][v]+dis1[j]);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<2;j++){
                ans=Math.min(ans,dis3[(1<<n)-1][i][j]);
            }
        }
        System.out.println(ans);
    }
    static double getDis(int a[],int b[]){
        return Math.sqrt(Math.pow(a[0]-b[0],2)+Math.pow(a[1]-b[1],2));
    }
}

E Sensor Optimization Dilemma 2

二分答案:对于每一个任务,要想使得用钱最少,一定优选择单位价钱较便宜的那个,但是最小值需要从0到100测试插入另一个机器的任务(鉴于数据范围不大于100,因此最多使用100次较贵的机器来做任务能造成至少一次的较便宜机器任务空缺),时间复杂度O(100nlog(1e9)*)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),x=sc.nextInt(),a[]=new int[n],p[]=new int[n],b[]=new int[n],q[]=new int[n];
        for(int i=0;i<n;i++){
            a[i]=sc.nextInt();
            p[i]=sc.nextInt();
            b[i]=sc.nextInt();
            q[i]=sc.nextInt();
        }
        int l=0,r=(int)1e9;
        while(l<r){
            int mid=(l+r)>>1;
            if(find(a,p,b,q,mid)<=x){
                l=mid;
            }
            else{
                r=mid-1;
            }
            if(l==r-1){
                if(find(a,p,b,q,r)<=x){
                    l=r;
                }
                break;
            }
        }
        System.out.println(l);
    }
    static long find(int a[],int p[],int b[],int q[],int w){
        if(w==0){
            return 0;
        }
        long ans=0;
        for(int i=0;i<a.length&&ans<=1e18;i++){
            if(a[i]*q[i]<b[i]*p[i]){
                a[i]^=b[i];
                b[i]^=a[i];
                a[i]^=b[i];
                p[i]^=q[i];
                q[i]^=p[i];
                p[i]^=q[i];
            }
            long min=((w-1)/a[i]+1)*(long)p[i];
            for(int j=1;j<=100;j++){
                long cur=q[i]*j+(b[i]*j>=w?0:((w-b[i]*j-1)/a[i]+1)*(long)p[i]);
                min=Math.min(min,cur);
            }
            ans+=min;
        }
        return ans;
    }
}

F Shipping

对于每一个最早出发时间,这一批最优的选择出发时间点无非是t+[0,n]*x,且最优的方案一定是运送相邻的连续一批,因此考虑的时间最多n^2种,离散化所有时间点后进行动态规划,时间复杂度O(n^4)

import java.util.*;
public class Main{
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),k=sc.nextInt(),x=sc.nextInt(),p=0;
        Map<Long,Integer> map1=new HashMap<>();
        long t[]=new long[n],map2[]=new long[n*n+5],ans[][]=new long[n+1][n*n+5];
        TreeSet<Long> set=new TreeSet<>();
        set.add(-1L*x);
        for(int i=0;i<n;i++){
            t[i]=sc.nextLong();
            for(int j=0;j<n;j++){
                set.add(t[i]+j*(long)x);
            }
        }
        for(long a:set){
            map1.put(a,p);
            map2[p]=a;
            p++;
        }
        for(int i=1;i<=n;i++){
            Arrays.fill(ans[i],(long)1e18);
        }
        for(int i=0;i<n;i++){
            for(int j=1;j<p;j++){
                ans[i+1][j]=ans[i+1][j-1];
                if(map2[j]<t[i]){
                    //运送时间不能早于货物最早可运送时间
                    continue;
                }
                int count=0,last=map1.get(set.floor(map2[j]-x));
                long sum=0;
                for(int w=i;w>=0;w--){
                    count++;
                    sum+=map2[j]-t[w];
                    if(count<=k){
                        ans[i+1][j]=Math.min(ans[i+1][j],ans[w][last]+sum);
                    }
                    else{
                        break;
                    }
                }
            }
        }
        System.out.println(ans[n][p-1]);
    }
}

G Only One Product Name

首尾相同的字符串可以组成连边,本题的题意即为,把环状的强连通分量缩点后,最少可以用多少路径覆盖,缩点利用tarjan算法,最少路径覆盖问题,可以考虑可达点之间的二分匹配,每匹配一对点,即可较少考虑一个路径,时间复杂度O(能过)

import java.util.*;
public class Main{
    static int timeStamp=0,sccNum=0;
    public static void main(String args[]){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),low[]=new int[n],time[]=new int[n],group[]=new int[n];
        String s[]=new String[n];
        List<Integer> path[]=new List[n];
        for(int i=0;i<n;i++){
            s[i]=sc.next();
            path[i]=new ArrayList<>();
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(s[i].charAt(1)==s[j].charAt(0)){
                    path[i].add(j);
                }
            }
        }
        Stack<Integer> stack=new Stack<>();
        boolean has[]=new boolean[n+5];
        for(int i=0;i<n;i++){
            if(time[i]==0){
                tarjan(i,path,stack,time,low,group,has);
            }
        }
        path=find(path,group);
        int ans=sccNum,idx[]=new int[n+5];
        for(int i=1;i<=sccNum;i++){
            if(hungary(path,idx,new boolean[n+5],i)){
                ans--;
            }
        }
        System.out.println(ans);
    }
    static List<Integer>[] find(List<Integer> path[],int group[]){
        //在已有的图上,把可达点之间全部连边,并输出新图
        Set<Integer> child[]=new Set[sccNum+5];
        List<Integer> ans[]=new List[sccNum+5];
        for(int i=1;i<=sccNum;i++){
            child[i]=new HashSet<>();
        }
        for(int i=0;i<group.length;i++){
            for(int a:path[i]){
                child[group[i]].add(group[a]);
            }
        }
        boolean has[]=new boolean[sccNum+5];
        for(int i=1;i<=sccNum;i++){
            allEdge(i,child,has);
            ans[i]=new ArrayList<>(child[i]);
        }
        return ans;
    }
    static void allEdge(int k,Set<Integer> child[],boolean has[]){
        //递归实现find函数
        if(!has[k]){
            has[k]=true;
            Set<Integer> set=new HashSet<>();
            for(int a:child[k]){
                if(!has[a]){
                    allEdge(a,child,has);
                }
                set.addAll(child[a]);
            }
            child[k].addAll(set);
            child[k].remove(k);
        }
    }
    static boolean hungary(List<Integer> path[],int idx[],boolean has[],int k){
        //匈牙利算法计算最大匹配
        for(int a:path[k]){
            if(has[a]){
                continue;
            }
            has[a]=true;
            if(idx[a]==0||hungary(path,idx,has,idx[a])){
                idx[a]=k;
                return true;
            }
        }
        return false;
    }
    static void tarjan(int k,List<Integer> path[],Stack<Integer> stack,int time[],int low[],int group[],boolean has[]){
        //塔扬算法缩点
        timeStamp++;
        time[k]=low[k]=timeStamp;
        stack.push(k);
        has[k]=true;
        for(int a:path[k]){
            if(time[a]==0){
                tarjan(a,path,stack,time,low,group,has);
                low[k]=Math.min(low[k],low[a]);
            }
            else if(has[a]){
                low[k]=Math.min(low[k],time[a]);
            }
        }
        if(time[k]==low[k]){
            sccNum++;
            while(true){
                int a=stack.pop();
                group[a]=sccNum;
                has[a]=false;
                if(a==k){
                    break;
                }
            }
        }
    }
}