629. K Inverse Pairs【Hard】 动态规划

作死试了一个看起来比较简单的HARD题,刚开始看到题目还觉得有点思路,然而后来却发现没法降低时间复杂度。


我的思路如下:每次知道了第n个状态下所有k的情况(即知道所有a[1,2..n][k]),则n+1的情况便可求出来。

  因为从n到n+1是相当于在原来的1,2..n的无序序列中加入n+1这个数,那么这个数,n+1,可以放置的位置有n+1位,

  例如n=4时,n+1=5,加入5的情况有    

    xxxx5  k+=0

    xxx5x  k+=1

    xx5xx  k+=2

    x5xxx  k+=3

    5xxxx  k+=4

  <n,k>下有a[n][k]个序列,那么就相当于给<n+1, k+i> (i=0,1,2,3,4)都贡献了a[n][k]个序列

  相当于给a[n+1][k] ,  a[n+1][k+1] , a[n+1][k+2] , a[n+1][k+3] , a[n+1][k+4]都加上了a[n][k]


  所以循环n次之后就能知道a[n][k]的值了

#include <iostream>
using namespace std;
const int md = 1000000007;
int a[1001][1001] = {{0}}, n, j, i, x, k;


int main() {
	cin >> n >> k;
	
	for (i = 1; i <= n; ++i) {
		if (i == 1) a[i][0] = 1;
		else for (j = 0; a[i - 1][j] > 0 && j <= k; ++j)
			for (x = 0; x < i && j + x <= k; ++x) {
				a[i][j + x] += a[i - 1][j];
				a[i][j + x] %= md;
			}
	}
	
	cout << a[n][k];
	
	return 0;
} 


看了网上的博客才知道这题可以用类似求等比数列的办法来算每个<n,k>的值,降低了复杂度,优化了算法。

原因是:由前面的推导,可以知道:

    a[n][k] = a[n - 1][k] + a[n - 1][k - 1] + ... + a[n - 1][k - n + 1]

用k-1替换k,就能得到:

    a[n][k - 1] = a[n - 1][k - 1] + a[n - 1][k - 2] + ... + a[n - 1][k - n]

一式减去二式,得到:

    a[n][k] = a[n][k - 1] + a[n - 1][k] - a[n - 1][k - n]

    当k>=n的时候,a[n - 1][k - n]才会有意义,所以要判断一下k和n的关系,如果k>=n的话,就要减去最后一项

这个递推式减少了一个循环,使算法更高效,代码如下:

class Solution {
public:
    int kInversePairs(int n, int k) {
        const int md = 1000000007;
        int a[1001][1001] = {{0}};
        int j, i;
		        
        a[0][0] = 1;
        for (i = 1; i <= n; ++i) {
            a[i][0] = 1;
            for (j = 1; j <= k; ++j) {
                a[i][j] = (a[i - 1][j] + a[i][j - 1]) % md;
                if (j >= i) {
                    a[i][j] = (a[i][j] - a[i - 1][j - i] + md) % md;
                }
            }
        }
        return a[n][k];
    }
};





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值