小胖办证 wzoi
小胖办证
题目描述:
xuzhenyi要办个签证。办证处是一座M层的大楼,1<=M<=100。
每层楼都有N个办公室,编号为1..N(1<=N<=500)。每个办公室有一个签证员。
签证需要让第M层的某个签证员盖章才有效。
每个签证员都要满足下面三个条件之一才会给xuzhenyi盖章:
- 这个签证员在1楼
- xuzhenyi的签证已经给这个签证员的正楼下(房间号相同)的签证员盖过章了。
- xuzhenyi的签证已经给这个签证员的相邻房间(房间号相差1,楼层相同)的签证员盖过章了。
每个签证员盖章都要收取一定费用,这个费用不超过1000000000。
找出费用最小的盖章路线,使签证生效
输入格式:
第1行两个整数M和N。
接下来M行每行N个整数,第i行第j个数表示第i层的第j个签证员收取的费用。
输出格式:
按顺序打出你经过的房间的编号,每行一个数。
如果有多条费用最小的路线,输出任意一条。
样例输入:
3 4 10 10 1 10 2 2 2 10 1 10 10 10
样例输出:
3 3 2 1 1
时间限制:1000ms
空间限制:256MByte
思路:
这道题本质是与数字三角形一样的,区别在于这道题需要记录路径并且要正着dp一次反着dp一次。实际上路径只要用一个二维数组记录每个点的父亲就行。
代码:
#include<bits/stdc++.h> #define ll long long using namespace std; struct node{ int x, y; node() {} node(int x1, int y_1) : x(x1), y(y_1) {} }b[111][511]; int n, m; ll a[111][511], f[111][511]; 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() { memset(f, 0x3f3f3f3f, sizeof(f)); 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++) f[1][i] = a[1][i]; for(int i = 2; i <= n; i++) { for(int j = 1; j <= m; j++) { ll n1 = f[i - 1][j]; ll n2 = f[i][j - 1]; ll n3 = f[i][j + 1]; if(n1 <= n2 && n1 <= n3) { f[i][j] = n1 + a[i][j]; b[i][j] = node(i - 1, j); } else if(n2 <= n1 && n2 <= n3) { f[i][j] = n2 + a[i][j]; b[i][j] = node(i, j - 1); } else { f[i][j] = n3 + a[i][j]; b[i][j] = node(i, j + 1); } } for(int j = m; j >= 1; j--) { ll n1 = f[i - 1][j]; ll n2 = f[i][j - 1]; ll n3 = f[i][j + 1]; if(n1 <= n2 && n1 <= n3) { f[i][j] = n1 + a[i][j]; b[i][j] = node(i - 1, j); } else if(n2 <= n1 && n2 <= n3) { f[i][j] = n2 + a[i][j]; b[i][j] = node(i, j - 1); } else { f[i][j] = n3 + a[i][j]; b[i][j] = node(i, j + 1); } } } ll mi = 9223372036854775807; int c; for(int i = 1; i <= m; i++) if(f[n][i] < mi) { mi = f[n][i]; c = i; } out(n, c); return 0; }