[NOIP模拟测试] 三角

【题目描述】

  ZGY 有一个三角,就像下面这样(每一个点都有一个权值)。

  第1 层有1 个,第2 层有2 个,第i 层有i 个。
  这个三角一共有n 层,ZGY 每次可以从第i 层的第j 个走到第i + 1 层的第j 个或是第j +1 个,直到走到第n 层。从第1 层走到第n 层的一种方案成为一条路径,路径的权值为路径上点权值之和。
  现在ZGY 想知道,权值前k 大的路径(存在多个正确答案)。

【输入格式】

  第一行,两个整数n, k 表示三角一共有n 层,ZGY 想知道权值前k 大的路径。

  接下来n 行:
  其中第i 行包含i 个整数,其中第j 个整数Wij 表示第i 层第j 个点权值为Wij

【输出格式】
  输出数据包含k 行,每行表示一条路径包含一个由“L” 和“R”组成的字符串,长
  度为n - 1 其中第i 个字符表示在第i 层时向下一层走的方向。假设当前在第i 行第j 个点,
  如果为“L”则走向第i + 1 行第j 个点,如果为“R”则走向第i + 1 行第j + 1 个点

解析:

60% :每个点用堆或数组记录前k大模拟即可。

100%:发现第k大的路径长度满足单调性。二分第k大路径的长度,每次check的时候查是否有k条路径的长度大于mid。问题变成了求图中第一层到最后一层有多少条路径长度大于某个值。怎么处理呢?预处理出每个点到最后一层的最大距离,记为f[i][j],跑dfs的时候如果已经经过的路径长度加上这个f[i][j]还小于mid值,直接return掉就ok了。

总结:

看到第k大不能只想到跟排序有关的算法,还应想到二分答案。

代码:

#include <cstdio>
#include <iostream>
using namespace std;

const int MAXN = 2005;
int w[MAXN][MAXN];
int n, k;
int f[MAXN][MAXN];
int mid, cnt;
char s[MAXN];
bool output = false;

inline int read() {
    int ret = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {if(c == '-') f = -f; c = getchar();}
    while(c >= '0' && c <= '9') {ret = ret * 10 + c - '0'; c = getchar();}
    return ret * f;
}

void prework() {
    for(int i = 1; i <= n; ++ i)
        f[n][i] = w[n][i];
    for(int t = n - 1; t >= 1; -- t) 
        for(int i = 1; i <= t; ++ i)
            f[t][i] = max(f[t + 1][i], f[t + 1][i + 1]) + w[t][i]; 
}

void dfs(int x, int y, int dis) {
    if(cnt >= k) return;
    if(dis + f[x][y] < mid) return; 
    if(x == n) {
        ++ cnt;
        if(output){
            for(int i = 1; i < n; ++ i)
                printf("%c", s[i]);
            printf("\n");
        }
        return;
    }
    s[x] = 'L';
    dfs(x + 1, y, dis + w[x][y]);
    s[x] = 'R';
    dfs(x + 1, y + 1, dis + w[x][y]);
}

int check() {
    cnt = 0;
    dfs(1, 1, 0);
    return cnt;
}

void solve() {
    int l = 0, r = 1000 * 1000 + 1;
    while(l < r) {
        mid = (l + r + 1) >> 1;
        if(check() >= k) l = mid;
        else r = mid - 1;
    }
    mid = l; output = true;
    cnt = 0;
    dfs(1, 1, 0);
}

int main() {
    freopen("tri.in", "r", stdin);
    freopen("tri.out", "w", stdout);
    n = read(); k = read();
    for(int i = 1; i <= n; ++ i) 
        for(int j = 1; j <= i; ++ j)
            w[i][j] = read();
    prework();
    solve();
    return 0;
} 
View Code

猜你喜欢

转载自www.cnblogs.com/JoshDun/p/11622846.html