牛客寒假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向后移动了。

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
*/

猜你喜欢

转载自www.cnblogs.com/fisherss/p/12316646.html
今日推荐