CodeForces 268D Wall Bars [基础DP]

Description

Manao is working for a construction company. Recently, an order came to build wall bars in a children's park. Manao was commissioned to develop a plan of construction, which will enable the company to save the most money.

After reviewing the formal specifications for the wall bars, Manao discovered a number of controversial requirements and decided to treat them to the company's advantage. His resulting design can be described as follows:

  • Let's introduce some unit of length. The construction center is a pole of height n.
  • At heights 1, 2, ..., n exactly one horizontal bar sticks out from the pole. Each bar sticks in one of four pre-fixed directions.
  • A child can move from one bar to another if the distance between them does not exceed h and they stick in the same direction. If a child is on the ground, he can climb onto any of the bars at height between 1 and h. In Manao's construction a child should be able to reach at least one of the bars at heights n - h + 1, n - h + 2, ..., n if he begins at the ground.
The figure to the left shows what a common set of wall bars looks like. The figure to the right shows Manao's construction

Manao is wondering how many distinct construction designs that satisfy his requirements exist. As this number can be rather large, print the remainder after dividing it by 1000000009 (109 + 9). Two designs are considered distinct if there is such height i, that the bars on the height i in these designs don't stick out in the same direction.


题意:

即在高度为N的杆子上,对于每一个高度x(1..N),恰好在四个方向之一有一个横杠,现在有一人其步长为h,即每一步只能在一个方向上继续上爬至多高度差为h的的横杠,问建造一个杆子,能让人至少沿一个方向上爬到(N-h+1)到N高度的方案数。


范围:

N<=1000,h<=30


解法

动态规划,DP[I][J][K][L][M]表示当前高度为I,四个方向上离I最近的横杠与I的差为J,K,L,M,其中J,K,L,M=0的话,表示差值大于了h,这样的话J,K,L,M的范围是0-30, 复杂度为9亿,需要继续优化,发现其中一个杆子的差值必然为0/1,那么只要 1000*30*30*30*2的复杂度就够啦,直接DP求解,我使用了一个滚动来优化空间。

(这时候I表示最近造的杆子所在的方向上是否合法,即0/1,如果合法,高度即为1,否则说明高度差曾经大于过h,然后K,L,M表示剩余的三个方向上的高度差值,然后,发现这三个方向上在DP的时候可以随便交换位置竟然也是对的……我一开始在这里卡了很久,不知道怎么转移,最后发现既然表示的是剩下的三个方向的高度差值,那么其顺序是无关的,所以不需要考虑那么多,直接DP即可....orz,果然DP还是练的太少)


代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdlib.h>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<bitset>
#pragma comment(linker, "/STACK:1024000000,1024000000")
template <class T>
bool scanff(T &ret){ //Faster Input
    char c; int sgn; T bit=0.1;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    if(c==' '||c=='\n'){ ret*=sgn; return 1; }
    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;
    ret*=sgn;
    return 1;
}
#define inf 1073741823
#define llinf 4611686018427387903LL
#define PI acos(-1.0)
#define lth (th<<1)
#define rth (th<<1|1)
#define rep(i,a,b) for(int i=int(a);i<=int(b);i++)
#define drep(i,a,b) for(int i=int(a);i>=int(b);i--)
#define gson(i,root) for(int i=ptx[root];~i;i=ed[i].next)
#define tdata int testnum;scanff(testnum);for(int cas=1;cas<=testnum;cas++)
#define mem(x,val) memset(x,val,sizeof(x))
#define mkp(a,b) make_pair(a,b)
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;

int pre[2][31][31][31];
int cur[2][31][31][31];

int mod=1000000009;

main(){
    int n,h,temp;
    scanff(n);
    scanff(h);
    if(h==1){
        printf("%d\n",4);
        return 0;
    }
    pre[1][2][2][2]=4;
    int ni,nj,nk,nl;
    rep(t,2,n){
        mem(cur,0);
        rep(i,0,1){
            ni=i+1;
            if(ni==1||ni==h+1)ni=0;
            rep(j,0,h){
                nj=j+1;
                if(nj==1||nj==h+1)nj=0;
                rep(k,0,h){
                    nk=k+1;
                    if(nk==1||nk==h+1)nk=0;
                    rep(l,0,h){
                        temp=pre[i][j][k][l];
                        if(temp==0)continue;
                        nl=l+1;
                        if(nl==1||nl==h+1)nl=0;
                        //这时候随意交换I,J,K,L的位置都是对的
                        //因为DP的内容与顺序无关,最后统计的时候只要看是否为正数
                        if(i)cur[1][nj][nk][nl]=(cur[1][nj][nk][nl]+temp)%mod;
                        else cur[0][nj][nk][nl]=(cur[0][nj][nk][nl]+temp)%mod;
                        if(j)cur[1][ni][nk][nl]=(cur[1][ni][nk][nl]+temp)%mod;
                        else cur[0][ni][nk][nl]=(cur[0][ni][nk][nl]+temp)%mod;
                        if(k)cur[1][ni][nj][nl]=(cur[1][ni][nj][nl]+temp)%mod;
                        else cur[0][ni][nj][nl]=(cur[0][ni][nj][nl]+temp)%mod;
                        if(l)cur[1][ni][nj][nk]=(cur[1][ni][nj][nk]+temp)%mod;
                        else cur[0][ni][nj][nk]=(cur[0][ni][nj][nk]+temp)%mod;
                    }
                }
            }
        }
        memcpy(pre,cur,sizeof(cur));
    }
    int ans=0;
    rep(i,0,1)
    rep(j,0,h)
    rep(k,0,h)
    rep(l,0,h){
        if(i||j||k||l)ans=(ans+cur[i][j][k][l])%mod;
    }
    printf("%d\n",ans);
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值