UVa 10564 沙漏里的路径(背包思想)

本文解析了一道类似于01背包问题的编程竞赛题目,通过动态规划的方法求解从矩阵的第一层走到最底层,所有路径中数字之和等于给定值的方案数量及最优路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:http://acm.hust.edu.cn/vjudge/contest/129795#problem/Q
题意:

要求从第一层走到最下面一层,只能往左下或右下走,经过的数字之和为Sum。问有多少条路径之和刚好等于Sum? 如果有的话,输出起点编号最小且字典序最小的路径。

分析:

一道类似01背包的题目,Sum相当于是背包容量,状态表示很容易想到:
f[i][j][S]表示从开始走到(i,j)这个位置剩余S的路径数
决策也是显然的:往左还是往右
对应上半段和下半段各自转移相应的状态即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 200 + 9;
int w[44][22], n, Sum, id, cnt;
ll f[44][22][555];
bool vis[44][22][555];
string res; 
/*
f[i][j][S]表示从开始走到(i,j)这个位置剩余S的路径数
决策也是显然的:往左还是往右
对应上半段和下半段各自转移相应的状态即可。
*/
ll dfs (int i, int j, int S, string path) {
    if (S < 0) return 0;
    if (S == 0 && i == n + n) {
        if (id == -1) id = cnt, res = path; //第一次到达的一定是编号最小和字典序最小的
        return 1;
    }
    if (i >= n + n) return 0;
    if (vis[i][j][S]) return f[i][j][S];
    vis[i][j][S] = 1;
    ll& ans = f[i][j][S];
    if (i < n) { //上半段
        if (w[i + 1][j - 1] != -1) ans += dfs (i + 1, j - 1, S - w[i][j], path + 'L');
        if (w[i + 1][j] != -1) ans += dfs (i + 1, j, S - w[i][j], path + 'R');
    } else { //下半段
        if (i + 1 == n + n) ans += dfs (i + 1, j, S - w[i][j], path);
        else if (w[i + 1][j] != -1) ans += dfs (i + 1, j, S - w[i][j], path + 'L');
        if (w[i + 1][j + 1] != -1) ans += dfs (i + 1, j + 1, S - w[i][j], path + 'R');
    }
    return ans;
}
int main() {
    //freopen ("f.txt", "r", stdin);

    while (~scanf ("%d%d", &n, &Sum) && (n + Sum) ) {
        memset (w, -1, sizeof (w) );
        memset (f, 0, sizeof (f) );
        memset (vis, 0, sizeof (vis) );
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n - i + 1; j++)
                scanf ("%d", &w[i][j]);
        for (int i = n + 1; i < n + n; i++)
            for (int j = 1; j <= i - n + 1; j++)
                scanf ("%d", &w[i][j]);

        id = -1;
        ll num = 0;
        cnt = 0;
        for (int j = 1; j <= n; j++, cnt++) num += dfs (1, j, Sum, "");
        printf ("%lld\n", num);
        if (num == 0) printf ("\n");
        else cout << id << ' ' << res << endl;
    }
    return 0;
}
/*

SampleInput
5 26
2 8 7 2 5
3 6 0 2
1 3 4
2 5
3
7 2
2 9 3
1 0 4 4
4 8 7 2 3
0 0
SampleOutput
5
2 RLLRRRLR
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值