POJ 1037 (计数 + DP) 一个美妙的栅栏

这道题总算勉勉强强看懂了,DP和计数都很不好想

DP部分:

称i根木棒的合法方案集合为S(i),第二根木棒比第一根长的方案称作UP方案,反之叫做DOWN方案

C[i][k][DOWN] 是S(i)中以第k短(而不是长度为k)的木棒打头的DOWN方案数。

假设S(i)中第一根木棒长为x,那么构成合法的方案数有两类:

  1. S(i - 1)中第一根木棒比x长的DOWN方案
  2. S(i - 1)中第一根木棒比x短的UP方案

有如下递推关系:

C[i][k][UP] = ∑ C[i-1][M][DOWN]
M = k ... i -1
C[i][k][DOWN] = ∑ C[i-1][N][UP]
N = 1… k-1

举个例子:

比如四根木棒,假设第一根木棒长度为2,在剩下的1 3 4中,比2长的3和4分别是S(3)中第2和第3短的。(要和C所定义的状态相对应)

所以上式的M和N的范围就是这样的

总的方案数就是:Sum{ C[n][k][DOWN] + C[n][k][UP] } k = 1.. n

计数部分:

扔掉这个题,考虑一下这个问题:

1 2 3 4全排列,求字典序的第10个

首先假设第一个数是1,那么后面有3! = 6 < 10种排列情况,所以打头的不是1。  继续假设是2,后面三个数也有6种排列情况, 6 + 6 ≥ 10,所以第一个数确定是2,此时跳过了第一个数为1的6种情况。

继续假设第二个数是1,后面有2! = 2种情况, 2 + 6 < 10,所以假设第二个数是3(注意2已经在第一个数中用过了),依旧是有2种情况, 8 + 2 ≥ 10,第二个数确定是3,跳过了10种情况。

后面因为10 ≥ 10,所以第三第四个数分别是1 4

所求的排列就是2 3 1 4

回到这个题上来:

前面已经求得了i个数中第k小打头的方案数,所以我们也完全可以模拟上面的思维过程来求解。

微调:以第i短的木棒作第k根时,有UP和DOWN两类方案,先用DOWN的方案数和C比较

 1 //#define LOCAL
 2 #include <iostream>
 3 #include <cstdio>
 4 #include <cstring>
 5 using namespace std;
 6
 7 const int maxn = 25;
 8 const int UP = 0;
 9 const int DOWN = 1;
10 long long C[maxn][maxn][2];
11
12 void Init(int n)
13 {
14     memset(C, 0, sizeof(C));
15     C[1][1][UP] = C[1][1][DOWN] = 1;
16     for(int i = 2; i <= n; ++i)
17         for(int k = 1; k <= i; ++k)
18         {
19             for(int M = k; M < i; ++M)
20                 C[i][k][UP] += C[i - 1][M][DOWN];
21             for(int N = 1; N <= k - 1; ++N)
22                 C[i][k][DOWN] += C[i - 1][N][UP];
23         }
24 }
25
26 void Print(int n, long long c)
27 {
28     long long skipped = 0;
29     int seq[maxn], used[maxn];
30     memset(used, 0, sizeof(used));
31     for(int i = 1; i <= n; ++i)
32     {
33         long long oldVal = skipped;
34         int k, No = 0;
35         for(k = 1; k <= n; ++k)
36         {
37             oldVal = skipped;
38             if(!used[k])
39             {
40                 ++No;
41                 if(i == 1)
42                     skipped += C[n][No][UP] + C[n][No][DOWN];
43                 else
44                 {
45                     if(k > seq[i - 1] && (i == 2 || seq[i - 2] > seq[i - 1]))
46                         skipped += C[n - i + 1][No][DOWN];
47                     else if(k < seq[i - 1] && (i == 2 || seq[i - 2] < seq[i - 1]))
48                         skipped += C[n - i + 1][No][UP];
49                 }
50                 if(skipped >= c)
51                     break;
52             }
53         }
54         used[k] = 1;
55         seq[i] = k;
56         skipped = oldVal;
57     }
58
59     for(int i = 1; i < n; ++i)    printf("%d ", seq[i]);
60     printf("%d\n", seq[n]);
61 }
62
63 int main(void)
64 {
65     #ifdef LOCAL
66         freopen("1037in.txt", "r", stdin);
67     #endif
68
69     int T, n;
70     long long c;
71     Init(20);
72     scanf("%d", &T);
73     while(T--)
74     {
75         scanf("%d %lld", &n, &c);
76         Print(n, c);
77     }
78     return 0;
79 }

代码君

时间: 2024-10-05 04:25:05

POJ 1037 (计数 + DP) 一个美妙的栅栏的相关文章

Ant Counting POJ 3046(计数dp)

原题 题目链接 题目分析 计数dp题,感觉其实也可以用组合数学做,但我太菜了,推不出通用公式.dp可以定义dp[i][j]为前i种选j个蚂蚁有多少种选法,然后递推公式如下,其中c[i]表示第i种的数量, dp[i][j]=Σ(min(j,c[i]),k=0)dp[i-1][j-k].可以化简一下,dp[i][j]=∑(c[i],k=0)dp[i-1][j-1-k]+dp[i-1][j]-dp[i-1][j-1-c[i]],最后答案就是dp[t][s]+dp[t][s+1]+...+dp[t][b

POJ 2229 计数DP

dp[i]代表是数字i的最多组合数如果i是一个奇数,i的任意一个组合都包含1,所以dp[i] = dp[i-1] 如果i是一个偶数,分两种情况讨论,一种是序列中包含1,因此dp[i]=dp[i-1]一种是序列不包含1,那么序列中的数都是2的倍数,把他们都除以2,序列与i/2序列相同,得到dp[i]=dp[i-1]+dp[i>>1] 1 #include <cstdio> 2 using namespace std; 3 int dp[1000000 + 10]; 4 int mai

poj 3342(树形dp)

题意:在一个公司中要举办一个聚会,每一个员工有一个奉献值.为了和谐规定直接上下级不能一起出席.让你找出奉献值之和最大为多少. 思路:dp[v][1]表示当前结点选,能获得的最大奉献值,dp[v][0]表示当前节点不选能获得的最大奉献值.状态转移: dp[v][0] = max(dp[v][0], ∑max(dp[x][1], dp[x][0]))x为直接儿子 dp[v][1] = max(dp[v][1], ∑dp[x][0] + vex[v]) 最后答案是max(dp[root][0], dp

[sdut]2879计数dp……或者递推

第五届省赛C:colourful cupcakes N=60. 天真如我,居然在考虑搜索的算法/(ㄒoㄒ)/~~三叉树……3^60=10^24+……不计算考虑复杂度都是耍流氓>_< 再算了一下,感觉O(N^4)可以试试,60^4=10^8+……但是毕竟最差的情况嘛>_<,再看一下题解果然是4重循环的……计数……dp……(想到之前坚信的搜索不禁(*/ω\*)) 中间看到了一个三次动规六个方程的算法. 做麻烦了. 学长思路好快. #include<iostream> #in

HDU4901 The Romantic Hero 计数DP

2014多校4的1005 题目:http://acm.hdu.edu.cn/showproblem.php?pid=4901 The Romantic Hero Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 393    Accepted Submission(s): 150 Problem Description There i

[计数dp] ural 1114. Boxes

题目链接: http://acm.timus.ru/problem.aspx?space=1&num=1114 1114. Boxes Time limit: 0.6 second Memory limit: 64 MB N boxes are lined up in a sequence (1 ≤ N ≤ 20). You have A red balls and B blue balls (0 ≤ A ≤ 15, 0 ≤ B ≤ 15). The red balls (and the blu

poj 1947(树形dp)

题意:一棵树上问你最少切掉几条边使得能分割出一个结点数正好为k的子树. 思路:dp[i][j]表示以i为根切掉j个结点最少要几条边. dp[v][j] = min(dp[v][j], dp[v][j-k] + dp[x][k]); 代码如下: 1 dp[v][j] = min(dp[v][j], dp[v][j-k] + dp[x][k]); 2 } 3 } 4 } 5 } 6 } 7 return vex[v]; 8 } 9 10 int main() 11 { 12 // freopen("

ACM/ICPC算法训练 之 数学很重要-浅谈“排列计数” (DP题-POJ1037)

这一题是最近在看Coursera的<算法与设计>的公开课时看到的一道较难的DP例题,之所以写下来,一方面是因为DP的状态我想了很久才想明白,所以借此记录,另一方面是看到这一题有运用到 排列计数 的方法,虽然排列计数的思路简单,但却是算法中一个数学优化的点睛之笔. Poj1037  A decorative fence 题意:有K组数据(1~100),每组数据给出总木棒数N(1~20)和一个排列数C(64位整型范围内),N个木棒长度各异,按照以下条件排列,并将所有可能结果进行字典序排序 1.每一

POJ 2486 树形DP

有一颗苹果树,每个节点上面有很多苹果,从一个节点到另外一个可以到达的节点花费1步,求k步最多能吃到多少苹果,起始点为1,可以不回到起始点. 这是典型的回溯型树状dp. dp[i][j][0]代表以i为根节点的子树最多j步后回到i能吃到的最多的苹果, dp[i][j][1]代表以i为根节点的子树最多j步后不回到i节点最多能吃到的子树.那么状态转移就分三步了. (1)dp[i][j+2][0] = max(dp[i][j+2][0], dp[i][j-k][0]+dp[son][k][0]); (2