预处理
题目一
即每个R左边都没有G
第一种方案:左边零个染成R
第二种方案:左边一个染成R
…
直到全部染成R
public int minPaintTest(String s){
char[] str=s.toCharArray();
int N=str.length;
int res=Integer.MAX_VALUE;
for(int L=0;L<=N;L++){
int help=0;
if(L==0){
for(int i=0;i<N;i++){
if(str[i]=='R'){
help++;
}
}
res=Math.min(res,help);
}else if(L==N){
for(int i=0;i<N;i++){
if(str[i]=='G'){
help++;
}
}
res=Math.min(res,help);
}else{
for(int i=0;i<=L;i++){
if(str[i]=='G'){
help++;
}
}
for(int i=L+1;i<=N;i++){
if(str[i]=='R'){
help++;
}
}
res=Math.min(res,help);
}
}
return res;
}
时间复杂度:O(N^2)
预处理数组
预处理:某一个操作频繁,但此操作可以省掉,依次优化时间复杂度
准备两个缓存数组(类似前缀和)
两个数组分别表示0-i上有几个G、i-N上有几个R
实际代码有优化
只准备一个数组即可
public int minPaint(String s){
if(s==null||s.length()<2){
return 0;
}
char[] chs=s.toCharArray();
int[] right=new int[chs.length];
right[chs.length-1]=chs[chs.length-1]=='R'?1:0;
for(int i=chs.length-2;i>=0;i--){
right[i]=right[i+1]+(chs[i]=='R'?1:0);
}
int res=right[0];//全换成G的情况
int left=0;
for(int i=0;i<chs.length-1;i++){
left+=chs[i]=='G'?1:0;
res=Math.min(res,left+right[i+1]);
}
res=Math.min(res,left+(chs[chs.length-1]=='G'?1:0));//全换成R的情况
return res;
}
题目二
一个N*N的矩阵
有O(N^4)数量级的长方形
有O(N^3)数量级的正方形(从左上角的点出发,锁定边长就可以确定一个正方形)
以上代码时间复杂度:O(N^4)
预处理
运用预处理简化遍历四条边的过程
生成两个对应的二维数组,一个表示每个数包括自己在内右边有几个连续的1
另外一个表示每个数包括自己在内下边有几个连续的1
处理过程:
拿到一个点(x,y)后到辅助数组中看该点右边连续的1的长度是否大于border,是的话就看(x+border,y)的位置的下方;同理看(x,y)的下方是的话就跳到(x,y+border)看右方
处理过程是O(1)
总时间复杂度:O(N^3)
public static int getMaxSize(int[][] m) {
int[][] right = new int[m.length][m[0].length];
int[][] down = new int[m.length][m[0].length];
setBorderMap(m, right, down);
for (int size = Math.min(m.length, m[0].length); size != 0; size--) {
if (hasSizeOfBorder(size, right, down)) {
return size;
}
}
return 0;
}
public static void setBorderMap(int[][] m, int[][] right, int[][] down) {
int r = m.length;
int c = m[0].length;
if (m[r - 1][c - 1] == 1) {
right[r - 1][c - 1] = 1;
down[r - 1][c - 1] = 1;
}
for (int i = r - 2; i != -1; i--) {
if (m[i][c - 1] == 1) {
right[i][c - 1] = 1;
down[i][c - 1] = down[i + 1][c - 1] + 1;
}
}
for (int i = c - 2; i != -1; i--) {
if (m[r - 1][i] == 1) {
right[r - 1][i] = right[r - 1][i + 1] + 1;
down[r - 1][i] = 1;
}
}
for (int i = r - 2; i != -1; i--) {
for (int j = c - 2; j != -1; j--) {
if (m[i][j] == 1) {
right[i][j] = right[i][j + 1] + 1;
down[i][j] = down[i + 1][j] + 1;
}
}
}
}
public static boolean hasSizeOfBorder(int size, int[][] right, int[][] down) {
for (int i = 0; i != right.length - size + 1; i++) {
for (int j = 0; j != right[0].length - size + 1; j++) {
if (right[i][j] >= size && down[i][j] >= size
&& right[i + size - 1][j] >= size
&& down[i][j + size - 1] >= size) {
return true;
}
}
}
return false;
}
题目三(补充:与预处理无关)
用二进制拼
先把给的f加工成0和1的发生器
返回(0-d-c)+c
public int f(){
return (int)(Math.random()*5)+1;
}
//等概率返回0和1的函数
public int r01(){
int res=0;
do{
res=f();
}while(res==3);
return res<3?0:1;
}
//等概率返回1-7的函数((0-6)+1)
//三个二进制位能代表0-7的数字
public int g(){
int res=0;
do{
res=(r01()<<2)+(r01()<<1)+r01();
}while(res==7);
return res+1;
}
第三问调用两次f函数,出现00和11循环,出现01和10的概率一样
public int g(){
int help1=-1;
int help2=-1;
do{
help1=f();
help2=f();
}while(help1==help2);
if(help1==0){
return 0;
}else{
return 1;
}
}