传送门:https://vjudge.net/problem/UVA-10564
题目大意:给你一张形如沙漏一般的图,每一个格子有一个权值,问你有多少种方案可以从第一行走到最后一行,并且输出起点最靠前的方案,以及此方案的起点编号,起点相同则字典序最小。
题解:
很容易想到一个DP,dp[i][j][S]代表到第i层,第j列,从第一层到这里的路径和为S的方案数,最后只要查询最后一行的方案数即可了。但是这样很不好输出方案!我们可能需要从终点向上dfs,但是又无法保证起点编号最小以及字典序最小。但是我们能从终点向上dfs,那么如果我们反过来DP呢?可行啊,这样就可以从起点向终点dfs了,贪心选择可行的路径即可。
需要注意的是上下两个一半的沙漏的转移一个是到j与j+1,一个是j与j-1,要考虑清楚细节!
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define RG register
#define LL long long
#define fre(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout);
using namespace std;
const int MAXN=,MAXS=;
int n;
LL ans,S;
LL a[MAXN*][MAXN],dp[MAXN*][MAXN][MAXS];
void dfs(int x,int y,int sum);
int main()
{
while(scanf("%d%lld",&n,&S)!=EOF)
{
if(n+S==)break;
for(int i=;i<=n;i++) for(int j=;j<=n-i+;j++) scanf("%lld",&a[i][j]);
for(int i=n+;i<*n;i++) for(int j=;j<=i-n+;j++) scanf("%lld",&a[i][j]);
memset(dp,,sizeof dp);
for(int i=;i<=n;i++) dp[n*-][i][a[n*-][i]]=;
for(int i=n*-;i>=n;i--)
for(int j=;j<=i-n+;j++)
for(int k=;k<=S;k++)
{
if(dp[i+][j][k] && k+a[i][j]<=S) dp[i][j][k+a[i][j]]+=dp[i+][j][k];
if(dp[i+][j+][k] && k+a[i][j]<=S) dp[i][j][k+a[i][j]]+=dp[i+][j+][k];
}
for(int i=n-;i>=;i--)
for(int j=;j<=n-i+;j++)
for(int k=;k<=S;k++)
{
if(dp[i+][j][k] && k+a[i][j]<=S) dp[i][j][k+a[i][j]]+=dp[i+][j][k];
if(dp[i+][j-][k] && k+a[i][j]<=S) dp[i][j][k+a[i][j]]+=dp[i+][j-][k];
}
ans=; for(int i=;i<=n;i++) ans+=dp[][i][S]; printf("%lld\n",ans);
for(int i=;i<=n;i++)
if(dp[][i][S])
{
printf("%d ",i-);
int x=,y=i,sum=S;
while(x<n*-)
{
if(x<n)
{
if(dp[x+][y-][sum-a[x][y]]) sum-=a[x][y],y--,printf("L");
else sum-=a[x][y],printf("R");
}
else
{
if(dp[x+][y][sum-a[x][y]]) sum-=a[x][y],printf("L");
else sum-=a[x][y],y++,printf("R");
}
x++;
}
break;
}
printf("\n");
}
return ;
}