牛客寒假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
*/