zoukankan      html  css  js  c++  java
  • HDU-4352 XHXJ's LIS 数位DP + 优化LIS + 状态压缩

    HDU-4352 XHXJ's LIS 数位DP + 优化LIS + 状态压缩

    一道很有教育意义的数位DP题目

    题意

    给定一个区间中,将区间的每一个数看成一个字符串,求这个区间内每个字符串的最大上升子序列等于(k)的个数。

    [0 leq L leq R leq 2^{63} - 1\ 1 leq k leq10 ]

    分析

    拿到题目几乎没什么思路。因为涉及到LIS,但是数位DP的时候显然没办法存储这么多DP数组。

    我们借鉴nlogn的LIS思想,考虑到最多只有10个数,不妨状态压缩用二进制的1表示该数计入LIS贡献,nlogn的LIS告诉我们每次只需找到第一个比当前数大的数并替换掉,就可以是等效的。

    于是只需要照这个思路写,DP多增加一维状态就好了。

    实际写的过程中,有以下问题:

    看似状态只需要设置pos,state即可,但是这样的坏处是每次计算都需要初始化dp数组,对于T组数据显然无法承受,此时只需再增加一维k表示处理LIS = k的个数即可只需要在while外面初始化一次

    此题需要考虑前导0带来的影响,因为前导0不能算入LIS的贡献

    常用的剪枝:当前1的个数大于k时,直接return即可

    代码

    #include<bits/stdc++.h>
    #define pii pair<long double,long double>
    #define eps 1e-7
    #define equals(a,b) (fabs(a - b) < eps)
    #define fi first
    #define se second
    using namespace std;
    typedef long long ll;
    
    const int maxn = 1e5 + 5;
    const ll MOD = 1e9 + 7;
    
    ll rd(){
    	ll x = 0;
    	int f = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9'){
    		if(ch == '-') f = -1;
    		ch = getchar();
    	}
    	while(ch >= '0' && ch <= '9'){
    		x = x * 10 + ch - '0';
    		ch = getchar();
    	}
    	return x * f;
    }
    
    
    int kase;
    int cur;
    vector<int> v;
    ll dp[25][(1 << 10) + 10][12];
    
    ll dfs(int pos,int st,int lim){
    	int num = __builtin_popcount(st);
    	if(pos < 0) return num == cur;
    	if(num > cur) return 0;
    	if(!lim && ~dp[pos][st][cur]) return dp[pos][st][cur];
    	int l = lim ? v[pos] : 9;
    	ll res = 0;
    	for(int i = 0;i <= l;i++){
    		if(!(st | i)) {
    			res += dfs(pos - 1,0,lim && i == l);
    			continue;
    		}
    		int j;
    		int f = 0;
    		for(j = i;(1 << j) <= st;j++){
    			if((1 << j) & st) {
    				f = 1;
    				res += dfs(pos - 1,(st ^ (1 << j)) | (1 << i),lim && i == l);
    				break;
    			}
    		}
    		if(!f) res += dfs(pos - 1,st | (1 << j),lim && i == l);
    	}
    	if(!lim) dp[pos][st][cur] = res;
    	return res;
    }
    
    ll cal(ll x){
    	v.clear();
    	while(x){
    		v.push_back(x % 10);
    		x /= 10;
    	}
    	return dfs(v.size() - 1,0,1);
    }
    
    int main(){
    	int T = rd();
    	memset(dp,-1,sizeof dp);
    	while(T--){
    		ll a = rd();
    		ll b = rd();
    		int k = rd();
    		cur = k;
    		printf("Case #%d: %lld
    ",++kase,cal(b) - cal(a - 1));
    	}
    }
    
  • 相关阅读:
    matlab 画图中线型及颜色设置
    创建二叉树求叶子节点个数
    乐视2016暑期实习编程题
    MFC特定函数的应用20160720(SystemParametersInfo,GetWindowRect,WriteProfileString,GetSystemMetrics)
    MFC使用ShowWindow(SW_MAXIMIZE)任务栏消失的处理
    Windows字符集的统一与转换
    MFC学习20160718(GetModuleFileName&&GetAppDataPath)
    泛型算法概述
    链表的特有算法操作
    流迭代器的使用
  • 原文地址:https://www.cnblogs.com/hznumqf/p/14449619.html
Copyright © 2011-2022 走看看