UVA 10564 Paths through the Hourglass(递推)

沙漏路径计数算法
本文介绍了一种求解特定沙漏形网格中路径数量的方法,通过动态规划算法实现,确保从起点到终点的路径上整数之和等于给定值。文章详细解释了状态转移方程,并提供了完整的代码实现。
题意:

有一个沙漏,从第一行开始走,每次往下走一行,往左或者往右走一列,不能走出沙漏。
你的目标是让沿途经过的所有整数之和恰好为一个给定的整数。输出符合条件的路径条数。

思路:

求路径条数,这很常见,令dp[i][j][s]表示以第i行,j列的元素为起点,累加的和为s的路径条数,先令终点的值为1,反向从终点向起点递推,求得答案。

状态:dp[i][j][k]从(i,j)出发路径上的和为k的路径数目。
状态转移:dp[i][j][k] = dp[i+1][j][k-v]+dp[i+1][j+1][k-v];

这题对于a[i][j]坐标构造很巧妙,参考了这个博客的代码。
http://hi.baidu.com/arosliu/item/e500c8e5b7a5a0c3bbf37d8e

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 50;
ll dp[50][50][505];
int n, s, a[N][N];
void print_path(int x,int y,int sum) {
    if(x == 2*n - 1)
        return;
    int v = a[x][y];
    if(dp[x+1][y][sum-v]) {
        putchar('L');
        print_path(x+1,y,sum-v);
    }else {
        putchar('R');
        print_path(x+1,y+1,sum-v);
    }
}
int main() {
    while(scanf("%d%d",&n,&s) != EOF && (n || s)) {
        for(int i = 1; i <= n; i++)
            for(int j = i; j <= n; j++) 
                scanf("%d",&a[i][j]);
        for(int i = n+1; i <= 2*n-1; i++)
            for(int j = n; j <= i; j++)
                scanf("%d",&a[i][j]);
        memset(dp,0,sizeof(dp));
        for(int i = n; i <= 2*n-1; i++) {
            int tmp = a[2*n-1][i];
            dp[2*n-1][i][tmp] = 1;
        }
        for(int i = 2*n-2; i >= n; i--) {
            for(int j = n; j <= i; j++) {
                int v = a[i][j];
                for(int k = a[i][j]; k <= s; k++)
                    dp[i][j][k] = dp[i+1][j][k-v] + dp[i+1][j+1][k-v];
            }
        }
        for(int i = n-1; i >= 1; i--) {
            for(int j = i; j <= n; j++) {
                int v = a[i][j];
                for(int k = a[i][j]; k <= s; k++) {
                    dp[i][j][k] = dp[i+1][j][k-v] + dp[i+1][j+1][k-v];
                }
            }
        }
        ll ans = 0;
        for(int i = 1; i <= n; i++) {
            ans += dp[1][i][s];
        }
        printf("%lld\n",ans);
        for(int i = 1; i <= n; i++) {
            if(dp[1][i][s]) {
                printf("%d ",i-1);
                print_path(1,i,s);
                break;
            }
        }
        printf("\n");
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值