zoukankan      html  css  js  c++  java
  • [题解] [AGC033D] Complexity

    题面

    题解

    (f[u][d][l][r])((u, l))((d, r)) 这个矩形最小的复杂度是多少
    那么转移就是

    [f[u][d][l][r] = egin{cases} max(f[u][k][l][r], f[k + 1][d][l][r])+1, k in [u, d - 1]\ max(f[u][d][l][k], f[u][d][k + 1][r]) + 1, k in [l, r - 1]\ end{cases} ]

    然而这是一个 (O(n^5)) 的 DP
    发现答案最大不会超过 (log(n) + log(m))
    考虑将复杂度这一维放到状态中来, 对状态中的某一维 DP
    (f[c][u][d][l]) 为复杂度为 (c) 时, 矩形最上面一行在 (u), 最下面一行在 (d), 以 (l) 为最左边一列能够延伸多少列
    发现竖着切是很简单的

    [f[c][u][d][l] = f[c - 1][u][d][l] + f[c - 1][u][d][l + f[c - 1][u][d][l]] ]

    横着切的较为复杂

    [f[c][u][d][l] = max(min(f[c - 1][u][k][l], f[c - 1][k + 1][d][l])), k in [u, d - 1] ]

    此时的复杂度是 (O(n^4log_n))
    考虑到随着 (k - u) 越来越大, (min) 式中左边的数单调不升, 右边的数单调不降
    (min) 式中左边的数为 (a), 右边的数为 (b)
    (a, b) 越来越接近的时候他们的 (min) 就会越大
    那么我们只需要二分出最后一个使得 (a > b)(k), 设其为 (k_1)
    那么只需要将 (k_1)(k_1 + 1)两个位置的取较大的那个即可
    复杂度变为 (O(n^3log_n^2))

    Code

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    const int N = 205; 
    using namespace std;
    
    int n, m, sum[N][N], f[2][N][N][N], ans; 
    char s[N]; 
    
    template < typename T >
    inline T read()
    {
    	T x = 0, w = 1; char c = getchar();
    	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * w; 
    }
    
    int calc(int u, int l, int d, int r)
    {
    	if(r < l || d < u) return 0; 
    	int res = (d - u + 1) * (r - l + 1), tmp; 
    	tmp = sum[d][r] - sum[d][l - 1] - sum[u - 1][r] + sum[u - 1][l - 1]; 
    	return tmp == res || !tmp; 
    }
    
    int binary(int opt, int u, int d, int k)
    {
    	int l = u, r = d - 1, mid, tmp1, tmp2, res = 0; 
    	if(l > r) return 0; 
    	while(l <= r)
    	{
    		mid = (l + r) >> 1;
    		tmp1 = f[opt][u][mid][k], tmp2 = f[opt][mid + 1][d][k]; 
    		res = max(res, min(tmp1, tmp2)); 
    		if(tmp1 < tmp2) r = mid - 1; 
    		else l = mid + 1; 
    	}
    	return res; 
    }
    
    void solve(int opt)
    {
    	for(int tmp, u = 1; u <= n; u++)
    		for(int d = u; d <= n; d++)
    			for(int l = 1; l <= m; l++)
    			{
    				tmp = f[opt ^ 1][u][d][l], f[opt][u][d][l] = tmp + f[opt ^ 1][u][d][l + tmp]; 
    				f[opt][u][d][l] = max(f[opt][u][d][l], binary(opt ^ 1, u, d, l)); 
    			}
    }
    
    int main()
    {
    	n = read <int> (), m = read <int> ();
    	for(int i = 1; i <= n; i++)
    	{
    		scanf("%s", s + 1);
    		for(int j = 1; j <= m; j++)
    			sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + (s[j] == '.'); 
    	}
    	for(int lst = m, u = 1; u <= n; u++)
    		for(int d = u; d <= n; d++, lst = m)
    			for(int l = m; l; l--)
    			{
    				while(lst >= l && !calc(u, l, d, lst)) lst--; 
    				f[0][u][d][l] = lst - l + 1; 
    			}
    	for(int i = 1; f[(i & 1) ^ 1][1][n][1] < m; i++)
    		solve(i & 1), ans = i; 
    	printf("%d
    ", ans); 
    	return 0; 
    }
    

    一点点小启示

    当需要被 DP 的值很小的时候, 不妨将其提到状态中去, 对状态中的某一个值进行 DP

  • 相关阅读:
    UIWebView的使用
    ASP.NET自定义简单分页
    匿名方法、委托和Lambda表达式区分演示(图文解释)
    Windows Phone开发(11):常用控件(下)
    来自CodeCanyo的15个优秀移动应用和工具
    JDK、J2EE、J2SE、J2ME概念详解
    配置Eclipse 实现按任意键代码自动补全
    ASP.NET验证码
    asp.net 压缩解压缩zip文件
    HOOK使用:全局键盘钩子
  • 原文地址:https://www.cnblogs.com/ztlztl/p/12796855.html
Copyright © 2011-2022 走看看