HDU 6787 Chess(线性动态规划)

这题是2020百度之星初赛第三场1005

题意描述

一个长度为n的棋盘( 1 ≤ n ≤ 1000 1\leq n\leq1000 1n1000),恰好放置m个传送门( 0 ≤ m ≤ 1000 0\leq m\leq1000 0m1000),传送门可以向之前的任意一点传送且如传送到的位置有传送门可连续传送,每次选手投掷骰子可以掷出1~11,即如果当前在位置 y 且投出数字 x,那棋子将会跳到位置 y+x。问有多少种设置棋盘的方式可以让选手从起点1走到终点n?

题目分析

1.起点1和终点n不能放置传送门
2.当前位置x放置传送门可传送到1~x-1位置,共x-1种传送门
3.因为掷骰子最大为11,连续放置传送门数量最大为10。
4.若传送门放不下,则输出-1。比如样例23 21,长度为23的棋盘最大放置20个传送门OXXXXXXXXXXOXXXXXXXXXXO,就像这样。

解题思路

1.简化问题第一步

当没有传送门时,问题转化为上楼梯问题,求方案数。动态转移方程: d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] + . . . + d p [ i − 11 ] ; dp[i]=dp[i-1]+dp[i-2]+...+dp[i-11]; dp[i]=dp[i1]+dp[i2]+...+dp[i11];

2.简化问题第二步

当传送门随意放置时,仅变化动态转移方程:
d p [ i ] = d p [ i − 1 ] ; dp[i]=dp[i-1]; dp[i]=dp[i1];
d p [ i ] = d p [ i ] + d p [ i − 2 ] ∗ ( i − 2 ) + . . . + d p [ i − x ] ∗ ( i − 2 ) ! ( i − x − 1 ) ! + . . . + d p [ i − 11 ] ∗ ( i − 2 ) ! ( i − 12 ) ! ; dp[i]=dp[i]+dp[i-2]*(i-2)+...+dp[i-x]*\frac{(i-2)!}{(i-x-1)!}+...+dp[i-11]*\frac{(i-2)!}{(i-12)!}; dp[i]=dp[i]+dp[i2](i2)+...+dp[ix](ix1)!(i2)!+...+dp[i11](i12)!(i2)!;
第一个方程解释:这里代表当前状态为未添加传送门的状态,未添加传送门时,为了避免重复计算,我们只让其从前一个位置的方案数转移过来,这样上一个位置可以从上上个位置转移,也就是说连续不放置传送门的时候方案数计算并不发生错误。
第二个方程解释:当放置传送门时,我们可以从 [ i − 2 , i − 11 ] [i-2,i-11] [i2,i11]这些位置转移过来,中间经过的位置放置满传送门,这时第 ( i − x ) (i-x) (ix)个位置到第 ( i ) (i) (i)个位置中间有 ( x − 1 ) (x-1) (x1)个空位,可以放置 ( x − 1 ) (x-1) (x1)个传送门,而每个位置 ( y ) (y) (y)可以放置传送门的种类数为 ( y − 1 ) (y-1) (y1)。这样也就是第 ( i − 1 ) (i-1) (i1)个可以放置 ( i − 2 ) (i-2) (i2)种传送门,从 ( i ) (i) (i)位置到 ( x ) (x) (x)位置共有 [ i − 1 , i − x + 1 ] [i-1,i-x+1] [i1,ix+1]这些个空位,传送门数量为 ( i − 2 ) ! ( i − x − 1 ) ! \frac{(i-2)!}{(i-x-1)!} (ix1)!(i2)!。此时需要将 d p dp dp转移从 ( i − x ) (i-x) (ix)位置转移到 ( i ) (i) (i)位置即可,动态转移方程写作 d p [ i ] = d p [ i ] + d p [ i − x ] ∗ ( i − 2 ) ! ( i − x − 1 ) ! dp[i]=dp[i]+dp[i-x]*\frac{(i-2)!}{(i-x-1)!} dp[i]=dp[i]+dp[ix](ix1)!(i2)!,而 [ i − 2 , i − 11 ] [i-2,i-11] [i2,i11]这些位置都需要进行转移,即所见方程二。

3.问题求解:

d p [ 当 前 位 置 ] [ 已 放 置 传 送 门 数 量 ] dp[当前位置][已放置传送门数量] dp[][],当走到位置i时,已放置j个传送门时,动态转移方程:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ; dp[i][j]=dp[i-1][j]; dp[i][j]=dp[i1][j];
d p [ i ] [ j ] = d p [ i ] [ j ] + d p [ i − 2 ] [ j − 1 ] ∗ ( i − 2 ) + . . . + d p [ i − x ] [ j − ( i − x − 1 ) ] ∗ ( i − 2 ) ! ( i − x − 1 ) ! + . . . + d p [ i − 11 ] [ j − 10 ] ∗ ( i − 2 ) ! ( i − 12 ) ! ; dp[i][j]=dp[i][j]+dp[i-2][j-1]*(i-2)+...+dp[i-x][j-(i-x-1)]*\frac{(i-2)!}{(i-x-1)!}+...+dp[i-11][j-10]*\frac{(i-2)!}{(i-12)!}; dp[i][j]=dp[i][j]+dp[i2][j1](i2)+...+dp[ix][j(ix1)](ix1)!(i2)!+...+dp[i11][j10](i12)!(i2)!;
这里注意为了避免重复计算,在计算中间不放置传送门时,只能从前一个位置转移方案数过来(这里并不会漏计算,从x到y,一格一格跳过来和从x直接跳到y方案数是一样的)。
而中间如果放置传送门时,从x到y,中间必须放满传送门,方案数用“x位置的方案数*中间每个位置能放置传送门的种类数”,注意要从x位置减去中间放置传送门数的那种方案来乘,也就是要从 d p [ x ] [ j − ( y − x − 1 ) ] dp[x][j-(y-x-1)] dp[x][j(yx1)]位置转移。

4.注意细节

注意无解情况,最多放置传送门需要让两个能走的位置中间不超过10个,用公式判断 ( m + ( m − 1 ) / 10 > n − 2 ) (m+(m-1)/10>n-2) (m+(m1)/10>n2)

附赠奇怪样例

input1

3
1 0
23 20
23 21
23 1000

output1

1
6622482
-1
-1

input2

5
5 0
5 1
5 2
5 3
5 4

output2

1
6
11
6
-1

input3

6
6 0
6 1
6 2
6 3
6 4
6 5

output3

1
10
35
50
24
-1

感谢胡同学写的暴力帮我找到WA点

#include <bits/stdc++.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <stack>
#include <algorithm>
#include <queue>
#include <map>
#include <cmath>
#define eps 0.00000001
#define pi P_MI
#define ll long long
using namespace std;
struct point {
    int x,y;
    bool operator < (const point &a) const {
        return x>a.x;//最小值优先
    }
};

priority_queue <int> que;

ll a[1100][1100];

int main () {
    int T;
    scanf("%d",&T);
    while (T--) {
        int n,m;
        scanf("%d%d",&n,&m);
        memset(a,0,sizeof(a));
        if (m+(m-1)/10>n-2){
            if (n==1 && m==0) printf("1\n");
            else printf("-1\n");
            continue;
        }
        a[1][0]=1;
        m=min(m,max(n-2,0));
        for (int i=2; i<=n; i++) {
            int mm=min(i-2,m);
            for (int j=0; j<=mm; j++) {
                a[i][j] = a[i-1][j];

                for (int k= max( i-j-1, max(1,i-11) ); k<=i-2; k++) {
                    ll sum=1;
                    for (ll l=k; l<=i-2; l++){
                        sum = sum * l % 1000000007;
                    }
                    a[i][j] = (a[i][j] + sum*a[k][j-(i-k-1)]) % 1000000007;
                }

            }
        }
        a[n][m]=a[n][m]%1000000007;
        printf("%lld\n",a[n][m]);
    }
    return 0;
}
//    freopen("1.in","r",stdin);
//    freopen("1.out","w",stdout);

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值