2018.10.05【校内模拟】上升序列(状压DP)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/82948236

【问题描述】

给出一个长度为 m 的上升序列 A(1 ≤ A[i]≤ n) , 请你求出有多少种 1…n 的排列, 满足
A是它的一个 LIS.

【输入】

第一行两个整数 n,m.
接下来一行 m 个整数, 表示 A.

【输出】

一行一个整数表示答案.

【输入样例 1】

5 3
1 3 4

【输出样例 1】

11

【输入样例 2】

4 2
3 4

【输出样例 2】

5

【数据范围与约定】

对于前 30% 的数据, n ≤ 9;
对于前 60% 的数据, n ≤ 12;
对于 100% 的数据, 1 ≤ m ≤ n ≤ 15.


解析:

不要想爆搜剪枝,你剪不动的,不然试试下面两组数据。

15 7
1 3 5 7 9 11 13
15 7
9 10 11 12 13 14 15

反正就是各种卡,除非特判
然而你特判的完吗?

思路:

这道题正解是状压DP

数据范围只有15,这很状压。
首先要知道最快的求 L I S LIS O ( n l o g n ) O(nlogn) 级别的做法。
很容易想到用 S S 表示目前选取的数的集合, S 1 S_1 表示目前 L I S LIS 的构成情况。
然而 O ( 2 n × 2 n ) O(2^n\times 2^n) 的空间复杂度阻止了我们前进的脚步。。。

m a p map一下? ,然而这个时间复杂度有阻止了我们前进的脚步。。。

好吧这道题需要一些高超的状压技巧。三进制。。。

先看我们总的方案数,实际上所有的 S 1 S_1 都是 S S 的自己,二项式展开一下得到 S 1 S_1 S S 的综合状态只有 3 n 3^n ,玄学三进制压一下就行了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
ll getint(){
	re ll num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=20;
cs int M=14348910;
int n,m;
int pow3[N],pow2[N];
int a[N];
ll f[M],ans;

inline
void dfs(int len,int dep,int v){
	if(len>n){
		if(!dep)ans+=f[v];
		return ;
	}
	v+=pow3[len-1];
	dfs(len+1,dep,v);
	v+=pow3[len-1];
	dfs(len+1,dep-1,v);
}

signed main(){
	n=getint();
	m=getint();
	for(int re i=1;i<=m;++i)a[i]=getint();
	pow2[0]=pow3[0]=1;
	for(int re i=1;i<=n;++i)pow2[i]=pow2[i-1]<<1,pow3[i]=pow3[i-1]*3;
	f[0]=1;
	for(int re i=0;i<=pow2[n]-2;++i){
		bool flag=1,tt=1;
		for(int re j=1;j<=m;++j){
			if((i&pow2[a[j]-1])==0)flag=false;
			else if(!flag){
				tt=false;
				break;
			}
		}
		if(!tt)continue;
		for(int re subset=i;;subset=(subset-1)&i){
			int now=0;
			for(int re j=1;j<=n;++j){
				if(subset&pow2[j-1])now+=pow3[j-1];
				if(i&pow2[j-1])now+=pow3[j-1];
			}
			if(f[now]){
				for(int re j=1;j<=n;++j){
					if(now/pow3[j-1]%3==0){
						int sta=now+2*pow3[j-1];
						for(int re k=j+1;k<=n;++k)
						if(now/pow3[k-1]%3==2){
							sta-=pow3[k-1];
							break;
						}
						f[sta]+=f[now];
					}
				}
			}
			if(!subset)break;
		}
	}
	dfs(1,m,0);
	cout<<ans;
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/82948236
今日推荐