湛江做网站设计公司中国十大品牌策划公司
牛客周赛85 DEF Java
- D小紫的优势博弈
- E小紫的线段染色
- F小紫的树上染色
D小紫的优势博弈
思路:枚举小红的所有情况,删除前1个,删除前2个 … 全部删除。这样得到了小紫的所有情况,然后从删除位置的后一个往后面数出现的‘0’和‘1’的个数,如果全是偶数就小紫赢。
这题我的思路是没问题:但是用了substring(),它的时间复杂度是O(k),这样一来总的时间复杂度是O(N*N) N=1e6,超出时间限制。其实没不必要用substring,内层循环直接这么写:for(int j=i;j<n;j++),一回事,这样时间复杂度降低了一点,也就通过了。
下面是超时代码:
修改后代码:
import java.util.*;
public class Main {public static void main(String[]args){Scanner scan=new Scanner(System.in);int n=scan.nextInt();//表示长度String s=scan.next();int cnt=0;int cnt0=0;int cnt1=0;for(int i=1;i<n;i++){cnt0=0;cnt1=0;for(int j=i;j<n;j++){if(s.charAt(j)=='0'){cnt0++;}else{cnt1++;}if(cnt0%2==0&&cnt1%2==0){cnt++;break;}}}double res=cnt*1.0/n;System.out.print(res);}
}
E小紫的线段染色
思路:
1.创建一个集合收集所有线段,该集合的元素类型是int类型的数组{l,r,次序}。
2.对所有线段排序,如果左边界不相同就按照左边界从小到大排序,左边界同,就按照右边界从小到大排序。
3.创建两个变量来记录此时最后红色线段的尾位置hongEnd和最后紫线段的尾位置ziEnd。
4.遍历所有线段,更新hongEnd和ziEnd,把涂成紫色的线段需要放入结果集合res中,最后看结果集合就可以了。
注意:优先选择ArrayList,因为LinkedList的get时间复杂度O(K)。
import java.util.*;
public class Main {public static void main(String[]args){Scanner scan=new Scanner(System.in);int n=scan.nextInt();//表示线段的数量//创建一个集合,元素类型是int型的数组,{l,r,次序},不能用LinkedList 因为get的时间复杂度尾O(K)ArrayList<Integer[]> list=new ArrayList();for(int i=0;i<n;i++){int l=scan.nextInt();int r=scan.nextInt();list.add(new Integer[]{l,r,i+1});}//对排序:如果两条线段的左边界相同,就按照右边界从小到大排序,如果左边界不同你就按照左边界从小到大排序Collections.sort(list,(o1,o2)->{//O(N*logN)if(o1[0].equals(o2[0])){return Integer.compare(o1[1],o2[1]);}else{return Integer.compare(o1[0],o2[0]);}});//创建存放结果的集合ArrayList<Integer> res=new ArrayList();//记录最后红色线段的尾位置int hongEnd=list.get(0)[1];//记录最后子线段的尾位置int ziEnd=-1;//遍历所有线段for(int i=1;i<n;i++){int curStart=list.get(i)[0];int curEnd=list.get(i)[1];//当前的线段是红色//有两种情况:1.当前的lefe<=hongEnd,就必须染成紫色。确定了要染成紫色,需要看看ziEnd是否<left,如果>left,就紫色相交了//2.当前的left大于hongend,就保持红色就好if(curStart<=hongEnd){//染成紫色if(curStart>ziEnd){//可以染res.add(list.get(i)[2]);ziEnd=curEnd;}else{//紫色相交了System.out.print(-1);return;}}else{//保持红色hongEnd=curEnd;}}if(res.size()==0) {res.add(1);}System.out.println(res.size());for(Integer i:res) {System.out.print(i+" ");}}
}
F小紫的树上染色
思路:题目求最大红色联通块的最小值,像这种用二分,最大红色块最小为0,最大为节点个数n,所以l=0,r=n。check(mid,k)定义为:在染紫次数<=k时,能使得最大红色联通块的个数为mid就返回true。check(mid,k)的具体操作:遍历这个无向图,从叶子结点开始(贪心思想),当该节点联通数量>mid就染紫色,记录一共染了多少个紫色,如果<=k就返回true。
步骤:
1.创建图,visit数组(遍历无向图用到),s数组(s[i]:与i节点联通的红色节点个数,加上自己)。
2.创建两个变量 l,r,开始二分查找。
import java.util.*;
public class Main {public static void main(String[]args){Scanner scan=new Scanner(System.in);int n=scan.nextInt();//节点个数int k=scan.nextInt();//最多染成紫色的个数visit=new boolean[n+1];//创建图graph=new ArrayList[n+1];for(int i=0;i<=n;i++) {graph[i]=new ArrayList<>();}for(int i=1;i<n;i++){int a=scan.nextInt();int b=scan.nextInt();graph[a].add(b);graph[b].add(a);}s=new int[n+1];int l=0,r=n;while(l<r){int mid=(l+r)>>1;need=0;Arrays.fill(visit, false);Arrays.fill(s, 0);if(check(mid,k)){//在<=k的机会内,使得最大红色联通块为midr=mid;}else{l=mid+1;}}System.out.println(l);} static ArrayList<Integer>[] graph;static int need;//需要的紫色个数static int[]s;//s[i]:与以i节点联通的红色节点个数加上自己//定义: 在<=k的机会之内,是否能让最大红色联通块大小为midstatic boolean check(int mid,int k){//从节点1开始遍历图dfs(1,mid,k);return need<=k;}static boolean visit[];//避免走回头路static void dfs(int root,int mid,int k) {visit[root]=true;for(int neibor:graph[root]) {if(visit[neibor]) {continue;}dfs(neibor,mid,k);//后序位置s[root]+=s[neibor];}//加上自己s[root]++;//判断红色联通块的数量是否大于midif(s[root]>mid) {//染紫色need++;s[root]=0;}}
}