zoukankan      html  css  js  c++  java
  • AGC033D

    题目链接:D - Complexity

    题目大意:洛谷


    题解:首先想到朴素的 DP,状态为左上角和右下角的坐标,状态数(O(n^4)),转移上看起来不太好优化,所以考虑优化状态。

    容易发现答案最大不超过(log n+log m),所以我们可以考虑一个经典的技巧,将状态与答案互换,即记录左上角和最下方的线的横坐标以及答案,左上角与最右方的线的纵坐标以及答案,这样的话状态数就变成了(O(n^3log n)),所以总时间复杂度(O(n^3log n))

    代码:

    #include <cstdio>
    int min(int a,int b){
    	return a<b?a:b;
    }
    int max(int a,int b){
    	return a>b?a:b;
    }
    const int Maxn=185;
    int n,m;
    char ch[Maxn+5][Maxn+5];
    int sum[Maxn+5][Maxn+5];
    int dp[2][Maxn+5][Maxn+5][Maxn+5];
    bool eq(int x1,int y1,int x2,int y2){
    	int siz=(x2-x1+1)*(y2-y1+1);
    	int c1=sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
    	return c1==siz||c1==0;
    }
    void init(){
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			sum[i][j]+=sum[i][j-1];
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=m;j++){
    			sum[i][j]+=sum[i-1][j];
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=i;j<=n;j++){
    			for(int l=1,r;l<=m;l=r){
    				r=l;
    				while(eq(i,l,j,r)&&r<=m){
    					r++;
    				}
    				for(int p=l;p<r;p++){
    					dp[0][i][j][p]=r-1;
    				}
    				if(l==r){
    					dp[0][i][j][l]=l-1;
    					r++;
    				}
    			}
    		}
    	}
    }
    int find(int c,int u,int d,int L){
    	int l=u,r=d-1,mid;
    	int res=0;
    	int v1,v2;
    	while(l<=r){
    		mid=(l+r)/2;
    		v1=dp[c][u][mid][L];
    		v2=dp[c][mid+1][d][L];
    		res=max(res,min(v1,v2));
    		if(v1<v2){
    			r=mid-1;
    		}
    		else{
    			l=mid+1;
    		}
    	}
    	return res;
    }
    void DP(int c){
    	int nv,lv;
    	for(int i=1;i<=n;i++){
    		for(int j=i;j<=n;j++){
    			for(int l=1;l<=m;l++){
    				lv=dp[c^1][i][j][l];
    				if(lv!=m){
    					nv=dp[c^1][i][j][lv+1];
    					nv=max(nv,find(c^1,i,j,l));
    				}
    				else{
    					nv=m;
    				}
    				dp[c][i][j][l]=nv;
    			}
    		}
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%s",ch[i]+1);
    		for(int j=1;j<=m;j++){
    			if(ch[i][j]=='#'){
    				sum[i][j]=1;
    			}
    		}
    	}
    	init();
    	int ans=0;
    	for(int i=1;dp[(i&1)^1][1][n][1]<m;i++){
    		DP(i&1);
    		ans=i;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    vscode配置远程开发环境
    C++条件语句和循环语句
    C++整数相除、取模运算和自加
    C++基础数据类型
    C++定义常量、标识符命名、整型数据类型
    Android压力测试工具Monkey简介
    adb shell命令后出现error: device not found报错解决方案
    xadmin修改登录页面背景图
    windbg如何让.cmdtree自动执行?
    Rabbitmq入门到进阶看这篇就够了!
  • 原文地址:https://www.cnblogs.com/withhope/p/13577885.html
Copyright © 2011-2022 走看看