uva10564(DP+路径打印)

这题的意思就是给出一个沙漏,第一行的两个数字n , s ;n就是沙漏的大小,最上面一行元素的个数,s就是目标权值.

要求问从第一行到最后一行所有路径中权值为s的路劲有几条.

然后输出字典序最小那条路是从第一行哪一个位置开始的(第一个是位置0),并输出这条路.

如果没有路径,输出一个空行.

首先,我们记(i,j)这个点权值为num[i][j] ,比如我们要知道从(i,j)这个位置到最后的权值为s的路径有多少条

那么我们要知道从(i + 1 , j) 这个位置到最后权值为s - num[i][j]的路径条数.

再加上从(i+ 1 , j -   1)这个位置到最后权值为s - num[i][j]的路径条数.

那么我们设f[i][j][s] 代表 从位置(i , j)到达最后,权值为s的路径有几条.

而且容易得到状态转移方程

f[i][j][s] = dp(i + 1 , j , s - num[i][j]) + dp(i + 1 , j - 1 , s -num[i][j]);
但是有一点需要特别注意,在沙漏上半部,j 往下是 j - 1 和 j

但是在沙漏下半部,j往下 是 j 和 j +1; 所以需要判断 i < n,来决定用哪一个.

然后是要打印路径,因为要字典序最小,从左往右找到第一个可行的路径递归打印出来.

打印时只要判断往左走是否可达,可达就往左,否则往右.(保证最小)

打印时也要注意是在上半边,还是下半边:


AC代码:


#include<stdio.h>
#include<string.h>
#define ll long long
const int N = 50;
int num[N][N];
int l[N][N];
int r[N][N];
ll f[N][N][N * 10];
int n,s,high;
ll res;
ll dp(int i , int j ,int s) {
	if(f[i][j][s] != -1)
		return f[i][j][s];
	if(num[i][j] == -1 || s < 0 )
		return 0;
	if(i == 2 * n - 1) {
		if(s == num[i][j])
			f[i][j][s] = 1;
		else
			f[i][j][s] = 0;
	}
	else {
		if(i < n)
			f[i][j][s] = dp(i + 1 , j , s - num[i][j]) + dp(i + 1 , j - 1 , s -num[i][j]);
		else
			f[i][j][s] = dp(i + 1 , j , s - num[i][j]) + dp(i + 1 , j + 1 , s -num[i][j]);
	}
	return f[i][j][s];
}
void print(int i , int j ,int s) {
	if(i == 2 * n -1) {
		printf("\n");
		return ;
	}
	if(i < n) {
		if(dp(i + 1 , j - 1, s - num[i][j]) > 0) {
			printf("L");
			print(i + 1 , j - 1 ,s - num[i][j]);
		}
		else {
			printf("R");
			print(i + 1 , j ,s - num[i][j]);
		}
	}
	else {
		if(dp(i + 1 , j, s - num[i][j]) > 0) {
			printf("L");
			print(i + 1 , j ,s - num[i][j]);
		}
		else {
			printf("R");
			print(i + 1 , j + 1 ,s - num[i][j]);
		}
		
	}
}
int main () {
	while(scanf("%d%d",&n,&s) && n + s) {
		memset(num, -1, sizeof(num));
		memset(f, -1 , sizeof(f));
		for(int i = 1; i < n; i++) {  
			for(int j = 1; j <= (n-i+1); j++) {  
				scanf("%d", &num[i][j]);  
			}  
		}  
		for(int i = 1; i <= n; i++) {  
			for(int j = 1; j <= i; j++) {  
				scanf("%d", &num[i+n-1][j]);  
			}  
		}  
		res = 0 ;
		for (int i = 1 ; i <= n ; i++) {
			res += dp(1 , i , s);
		}
		printf("%lld\n",res);
		if(res == 0)
			printf("\n");
		else {
			for (int i = 1 ; ; i++) {
				if(f[1][i][s] != 0) {
					printf("%d ",i - 1);
					print(1 , i , s);
					break;
				}
			}
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值