《DP》小胖办证

Problem B. 小胖办证

时间限制 1000 ms
内存限制 128 MB

题目描述

xuzhenyi要办个签证。办证处是一座M层的大楼,1< =M< =100。
每层楼都有N个办公室,编号为1..N(1< =N< =500)。每个办公室有一个签证员。
签证需要让第M层的某个签证员盖章才有效。
每个签证员都要满足下面三个条件之一才会给xuzhenyi盖章:

  1. 这个签证员在1楼
  2. xuzhenyi的签证已经给这个签证员的正楼下(房间号相同)的签证员盖过章了。
  3. xuzhenyi的签证已经给这个签证员的相邻房间(房间号相差1,楼层相同)的签证员盖过章了。

每个签证员盖章都要收取一定费用,这个费用不超过1000000000。
找出费用最小的盖章路线,使签证生效

输入数据

第 11 行两个整数 MM 和 NN 。
接下来 MM 行每行 NN 个整数,第 ii 行第 jj 个数表示第 ii 层的第 jj 个签证员收取的费用。

输出数据

按顺序打出你经过的房间的编号,每行一个数。
如果有多条费用最小的路线,输出任意一条。

样例输入

3 4
10 10 1 10
2 2 2 10
1 10 10 10

样例输出

3
3
2
1
1

思路分析: 

dp 正反各一遍,保证左右可以互相更新,路径的话,建立node数组保存子节点,到时候直接查找就可以。


AC代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<math.h>
#define INF 0x7FFFFFFF //int 最大值
#define LLINF 9223372036854775807 //long long 最大值
#define maxN 505
#define maxM 105
#define ll long long
using namespace std;

// dp 正反各一遍,保证左右可以互相更新
//然后建立node保存子节点,之后遍历即可
//保存子节点
struct node{
    int x, y;
    node() {}
    node(int x1, int y1){
        this->x=x1;
        this->y=y1;
    }
}b[maxM][maxN];

int n, m;
long long a[maxM][maxN], dp[maxM][maxN];

void out(int x, int y)
{
    if(x == 0 || y == 0) return ;
    out(b[x][y].x, b[x][y].y);
    printf("%d\n", y);
}

int main()
{
    for(int i=0;i<maxM;i++)//设置最大值,即使越界也会保持结果正确
        for(int j=0;j<maxN;j++)
            dp[i][j]=LLINF;
    //memset(dp[i], LLINF, sizeof(dp[i]));//memset:会报错,数据范围太大。
    //cout<<dp[0][0]<<" "<<dp[1][1]<<endl;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            scanf("%d", &a[i][j]);
    for(int i = 1; i <= m; i++)//初始化第一层
        dp[1][i] = a[1][i];
    for(int i = 2; i <= n; i++)//两次dp
    {
        for(int j = 1; j <= m; j++)
        {
            ll n1 = dp[i - 1][j];
            ll n2 = dp[i][j - 1];
            ll n3 = dp[i][j + 1];
            if(n1 <= n2 && n1 <= n3)
            {
                dp[i][j] = n1 + a[i][j];
                b[i][j] = node(i - 1, j);
            }
            else if(n2 <= n1 && n2 <= n3)
            {
                dp[i][j] = n2 + a[i][j];
                b[i][j] = node(i, j - 1);
            }
            else
            {
                dp[i][j] = n3 + a[i][j];
                b[i][j] = node(i, j + 1);
            }
        }
        for(int j = m; j >= 1; j--)
        {
            ll n1 = dp[i - 1][j];
            ll n2 = dp[i][j - 1];
            ll n3 = dp[i][j + 1];
            if(n1 <= n2 && n1 <= n3)
            {
                dp[i][j] = n1 + a[i][j];
                b[i][j] = node(i - 1, j);
            }
            else if(n2 <= n1 && n2 <= n3)
            {
                dp[i][j] = n2 + a[i][j];
                b[i][j] = node(i, j - 1);
            }
            else
            {
                dp[i][j] = n3 + a[i][j];
                b[i][j] = node(i, j + 1);
            }
        }
    }
    ll minR = LLINF;
    int index;
    for(int i = 1; i <= m; i++)
        if(dp[n][i] < minR)
        {
            minR = dp[n][i];
            index = i;
        }
    out(n, index);
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值