牛客寒假6-B重排列| dp计数

题目地址:https://ac.nowcoder.com/acm/contest/3007/D

思路

1.无后效性:假设前i-1个位置已经找好,那么前i-1个位置的值就不再影响后面了
2.那么我们考虑第i个位置的方案,只需要找a数组中后面比b[i]大的个数就是当前第i个位置能放的种类数。
找方案数,用乘法原理。
状态转移方程:dp[i] = dp[i-1] * a数组中i~n中比b[i]大的数的个数

3.然后进行优化:
a数组中i~n中比b[i]大的数的个数,
可以用二分查找O(nlogn)在a数组中查找比b[i]大的个数,
也可以用双指针,因为先对a\b数组排序,查找比b[i]大的个数时,肯定是从已经找到比b[i-1]大的的指针j向后移动了。

4.边界
dp[0] = 1;

AC代码

方法一:dp + 双指针
时间复杂度O(2n)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e5+10;
const ll mod = 1e9+7;

ll a[maxn],b[maxn];
ll dp[maxn];
int n;

int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	sort(a+1,a+1+n);
	sort(b+1,b+1+n);
	int j = 0;
	dp[0] = 1;
	//O(2n)
	for(int i=1;i<=n;i++){
		while(j < n && a[j+1] <= b[i]) j++;
		dp[i] = dp[i-1] * max(0,j-i+1) % mod;
	}
	cout<<(dp[n]+mod)%mod;
	return 0;
}

方法二:dp + 二分
时间复杂度O(nlogn)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1e5+10;
int n;
ll a[maxn],b[maxn],dp[maxn];

int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	sort(a+1,a+n+1);
	sort(b+1,b+n+1);
	dp[0] = 1; 
	//O(nlogn)
	for(int i=1;i<=n;i++){
		int r = upper_bound(a+1+i-1,a+n+1,b[i]) - a - 1; //二分查找比b[i]大的 
		if(r > n) r = 0; //没查找到r会>n 此时令r = 0 说明不合法 
		dp[i] = (dp[i-1] * max(0,r-i+1)) % mod;
	}
	cout<<(dp[n]+mod)%mod;
	return 0;
}

/*
4
1 1 2 3
1 2 3 4
*/
posted @ 2020-02-16 13:58  fishers  阅读(130)  评论(0编辑  收藏  举报