动态规划:消除后效性
Posted sun897949163
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划:消除后效性相关的知识,希望对你有一定的参考价值。
无后效性:
这是DP中最重要的一点, 他要求每个子问题的决策不能对后面其他未解决的问题产影响, 如果产生就无法保证决策的最优性, 这就是无后效性。往往需要我们找到一个合适的状态。上述的问题还有另外一个描述方式, 对于后一个节点的判断不能以前面节点的路径为依据。
例:POJ 1037 一个美妙的栅栏
N 个木棒, 长度分别为1, 2, …, N.
构成美妙的栅栏要求
1.除了两端的木棒外,每一跟木棒,要么比它左右的两根都长,要么比它左右的两根都短。
2.即木棒呈现波浪状分布,这一根比上一根长了,那下一根就比这一根短,或反过来
如下图就是N等于4时的所有可能性
问题:
符合上述条件的栅栏建法有很多种,对于满足条件的所有栅栏, 按照字典序(从左到右, 从低到高) 排序。
给定一个栅栏的排序号,请输出该栅栏, 即每一个木棒的长度.
问题抽象:
问题抽象:给定1到N 这N个数字,将这些数字高低交替进行排列 ,把所有符合情况的进行一个字典序排列,问第C个排列是一个怎样的排列
试着解题
按照上文的DP常用方法我们依次做如下分析:
·
·
直接将问题的范围缩小
设 A[i] 为i根木棒所组成的合法方案数目。看看能否找出A[i]和A[i-1]或A[i-j]之间的递推关系(所有木棒总数是i)。称i根木棒的合法方案集合为S(i)。
对于这种方法, 我们可以想到, 在i种木棍的情况下, 假设我们有N中方案数。 能否在这N种方案的最后插入第i+1根木棒, 或者如何在这I根中插入第i+1根,也跟前面的选择方案有关, 所以在这里, 这种简单粗暴的方式并不具备后效性(但不得不说 在水DP中这依旧是对绝大多数题目有效的方法)
·
·
加限制条件
在选定了某根木棒x作为第一根木棒的情况下,剩下i-1根木棒的合法方案数是A[i-1]。但是,这A[i-1]种方案,并不是每种都能和x形成新的合法方案。将第一根比第二根长的方案称为DOWN方案,第一根比第二根短的称为UP方案,则,S(i-1)中,第一根木棒比x长的DOWN方案,以及第一根木棒比x短的UP方案,才能和x构成S(i)中的方案。
这里我们对木棒的条件进行条件限制, 在加入第i根木棒的情况下我们记录当前情况的上升和下降的情况。
·
·
继续推导
置A[i] = 0。先枚举x。然后针对每个x,枚举x后面的那根木棒y。如果 y > x(x < y的情况类推),则:
A[i] += 以y打头的DOWN方案数
但以y打头的DOWN方案数,又和y的长短有关。
于是难以直接从 A[i-1]或 A[i-j]推出 A[i]
也就是这里依旧不具备后效性
·
·
·
继续加限制条件
考虑将A[i]这种粗略的状态描述方式细化,即加上限制条件后分类。设
A[i] = Σ B[i][k] k = 1….i
B[i][k] 是S(i)中以第k短的木棒打头的方案数。
尝试对 B 进行动归。第k短,指的是i根木棒中第k短。
·
·
·
综合上述的全部限制条件
B[i][k] = Σ B[i-1][M](D OWN) + Σ B[i-1][N](U P)
M = k … i-1 , N = 1… k-1
还是没法直接推。于是把B再分类细化:
B[i][k] = C[i][k][D OWN] + C[i][k][U P]
C[i][k][DOWN] 是S(i)中以第k短的木棒打头的DOWN方案数。然后试图对C进行动归
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
初始条件:C[1][1][UP]=C[1][1][DOWN] = 1
思路总结
当选取的状态,难以进行递推时(分解出的子问题和原问题形式不一样,或不具有无后效性),考虑将状态增加限制条件后分类细化,即增加维度,然后在新的状态上尝试递推
另外这题目的另一个难点就是关于排列计数的问题, 这个问题在这里暂时不讲
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 25
using namespace std;
long long dp[maxn][maxn][2];
void init(int n)
memset(dp, 0, sizeof(dp));
dp[1][1][0] = 1;//up
dp[1][1][1] = 1;//down
for (int j = 2; j <= n; j++)
for (int i = 1; i <= j; i++)
for (int x = i; x < j; x++)
dp[i][j][0] += dp[x][j - 1][1];
for (int x = 1; x < i; x++)
dp[i][j][1] += dp[x][j - 1][0];
int ans[maxn];
void solve(int n, long long c)
//下面主要是排列组合的相关问题
int tn = n;
int flag, cur;
long long sum = 0LL;
for (int i = 1; i <= n; i++)
if (sum + dp[i][n][0] + dp[i][n][1] >= c)
ans[1] = i;
c -= sum;
cur = i;
break;
sum += (dp[i][n][0] + dp[i][n][1]);
if (c < dp[cur][n][1])
flag = 1;
else
c -= dp[cur][n][1];
flag = 0;
--n;
int len = 2;
while (n > 0)
if (flag == 0)
for (int i = cur; i <= n; i++)
if (dp[i][n][1] >= c)
cur = i;
ans[len++] = cur;
break;
c -= dp[i][n][1];
else
for (int i = 1; i<cur; i++)
if (dp[i][n][0] >= c)
cur = i;
ans[len++] = cur;
break;
c -= dp[i][n][0];
--n;
flag = 1 - flag;
int vis[maxn] = 0 ;
for (int i = 1; i <= tn; i++)
for (int j = 1; j <= tn; j++)
if (vis[j] == 0)
ans[i]--;
if (ans[i] == 0)
printf("%d%c", j, i == tn ? '\\n' : ' ');
vis[j] = 1;
break;
int main(void)
int T, n;
long long c;
scanf("%d", &T);
while (T--)
scanf("%d %lld", &n, &c);
solve(n, c);
return 0;
以上是关于动态规划:消除后效性的主要内容,如果未能解决你的问题,请参考以下文章