5、不等式数列--百度2017春招

[编程题] 不等式数列
时间限制:1秒
空间限制:32768K
度度熊最近对全排列特别感兴趣,对于1到n的一个排列,度度熊发现可以在中间根据大小关系插入合适的大于和小于符号(即 '>' 和 '<' )使其成为一个合法的不等式数列。但是现在度度熊手中只有k个小于符号即('<'')和n-k-1个大于符号(即'>'),度度熊想知道对于1至n任意的排列中有多少个排列可以使用这些符号使其为合法的不等式数列。 
输入描述:
输入包括一行,包含两个整数n和k(k < n ≤ 1000)
 
 
输出描述:
输出满足条件的排列数,答案对2017取模。
 
输入例子:
5 2
 
输出例子:
66
 
解题思路:首先想到的是暴力的方式解决该问题
1)求出所有1-n的数的全排列分别存入vector中
2)对每一种排列,如果前一个小于后一个,小于数目--;如果大于则,大于数目--
3)如果一次操作过后,xiaoyu为0 && dayu为0  则符合条件,result++
4)输出result%2017
 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 
 6 void swap(int* p1,int* p2)
 7 {
 8     int temp = *p1;
 9     *p1 = *p2;
10     *p2 = temp;
11 }
12 vector<vector<int> > res;
13 //获得1-n 全排列的个数,并存储全排列结果
14 void recall(int* arr,int len,int index,int *count)
15 {
16     vector<int> tmp;
17     if (index<=0)
18     {
19         (*count)++;//由于优先级的关系,记得加括号
20         for(int i=0;i<len;i++)
21         {
22             //cout<<arr[i]<<" ";
23             tmp.push_back(arr[i]);
24         }
25         res.push_back(tmp);
26         tmp.clear();
27         //cout<<endl;
28         return;
29     }
30     for (int i=index;i>=0;i--)
31     {
32         swap(&arr[index],&arr[i]);
33         recall(arr,len,index-1,count);
34         swap(&arr[index],&arr[i]);
35     }
36 }
37 
38 
39 int main()
40 {
41     int n;
42     int k;
43     while(cin>>n>>k)
44     {
45         int count = 0;
46         int result = 0;
47         int a[n];
48 
49         for(int i=0;i<n;i++)
50         {
51             a[i] = i+1;
52         }
53         recall(a,n,n-1,&count);
54         //cout<<count<<endl;
55         for(int i=0;i<count;i++)
56         {
57             int xiaoyu = k;
58             int dayu = n - k - 1;
59             for(int j=0;j<n-1;j++)
60             {
61                 //cout<<res[i][j]<<"  ";
62                 if(res[i][j]< res[i][j+1])
63                 {
64                     xiaoyu--;
65                 }
66                 else if(res[i][j]>res[i][j+1])
67                 {
68                     dayu--;
69                 }
70             }
71             //cout<<endl;
72             //cout<<"k"<<xiaoyu<<"add"<<dayu<<endl;
73             if(xiaoyu==0 && dayu == 0)
74             {
75                 result++;
76             }
77         }
78         cout<<result%2017<<endl;
79     }
80     return 0;
81 }

正确解法:
递归公式:dp[i][j] = (dp[i - 1][j - 1] * (i - j) + dp[i - 1][j] * (j + 1)) % 2017;
dp[i][j]表示有i个数字及j个小于号所能组成的数量(大于号数量当然是i - j - 1次,后面需要使用)
而加入第i + 1个数字时,分以下四种情况:
1、如果将i+1插入当前序列的开头,即有了1<2,加入后成为3>1<2,会发现等于同时加入了一个大于号
2、如果将i+1插入当前序列末尾,即1<2变成了 1<2<3,会发现等于同时加入了一个小于号
3、如果将i+1加入一个小于号之间,即已经有 1<2了,向中间加入3,会发现变成了1<3>2,等于同时加入了一个大于号
4、如果将i+1加入一个大于号中间,即有了2>1,变成了2<3>1,等于同时加入了一个小于号
综上所述,dp[i][j]等于以上四种情况之和:
dp[i - 1][j]         //将i加在开头等于加入一个大于号,即要求i-1个数时已经有了j个小于号
dp[i - 1][j - 1]    //将i加在末尾等于加入一个小于号,即要求i-1个数时已经有了j-1个小于号
dp[i - 1][j] * j    //将i加在任意一个小于号之间,等于加入了一个大于号;即要求i-1个数时已经有了j个小于号,每个小于号都可以进行这样的一次插入
dp[i - 1][j - 1] * (i- j - 1)    //将i加在任意一个大于号之间,等于加入了一个小于号;即要求i-1个数时有了j-1个小于号,而此时共有(i - 1) - (j - 1)- 1个大于号,每个大于号都要进行一次这样的操作
合并同类项即为
dp[i][j] = (dp[i - 1][j - 1] * (i - j) + dp[i - 1][j] * (j + 1)) 
最后要记得取模。。。
初始化dp[i][0] = 1  因为全部为大于号时,只有一种排列方式;
 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 int main()
 6 {
 7     int dp[1005][1005];
 8     int n, k;
 9     while(cin>>n>>k)
10     {
11         for(int i = 1; i <= n; i++)
12         {
13             dp[i][0] = 1;
14         }
15         for(int i = 2; i <= n; i++)
16         {
17             for(int j = 1; j <= k; j++)
18             {
19                 dp[i][j] = (dp[i - 1][j - 1] * (i - j) + dp[i - 1][j] * (j + 1)) % 2017;
20             }
21         }
22         cout << dp[n][k] % 2017 << endl;
23     }
24     return 0;
25 }

 
原文地址:https://www.cnblogs.com/qqky/p/6899628.html