USACO历年白银组真题解析 | 2023年1月Following Directions

学习C++从娃娃抓起!记录下USACO(美国信息学奥赛)备考白银组别比赛学习过程中的题目,记录每一个瞬间。

附上汇总贴:USACO历年白银组真题解析 | 汇总-CSDN博客


【题目描述】

Farmer John 有一个正方形的草地,草地被划分为了 (N+1)×(N+1)(1≤N≤1500) 的格子。设 (i,j) 为从上到下、从左到右第 i 行,第 j 列的格子。每个满足 1≤i,jn 的格子 (i,j) 之中都住着一头牛,而且每个这样的格子上都有一个路标指向右或下。除此之外,所有满足 i=N+1 或 j=N+1 的格子,除了 (N+1,N+1) 都会有一个饲料桶。牛在每个饲料桶进食需要的价格不同;位置 (i,j) 上的桶喂饱一只牛需要价格 ci,j(1≤ci,j≤500)。

每天晚饭时间,Farmer John 摇响晚餐铃时,所有牛都沿着路标的指向前进,直到它们遇到了饲料桶,之后它们会在它们自己遇到的饲料桶那里进食。第二天,所有牛又会回到自己原来的位置。

为了维持预算,Farmer John 想要知道每天喂食需要的价钱。然而,每天晚饭之前,总会有一头牛 (i,j) 翻转它那里的路标(原来向下则变成向右,反之亦然)。被翻转的路标指向将在后面的日子里保持不变,除非它又被进行了翻转。

给出每天被翻转的路标的坐标,请输出每天喂食需要的价格(总共有 Q 天,1≤Q≤1500)。

【输入】

第一行为 N(1≤N≤1500)

接下来的 N+1 行从上到下输入初始的路标朝向和每个饲料桶的价格 ci,j。前 N 行每行包含一个长度为 N 的字符串,其中每个字符只能是 R 或 D(R 表示向右,D 表示向下),之后是一个数,表示价格 ci,N+1,第 (N+1) 行包含 N 个数,依次表示价格 cN+1,j

接下来的一行为 Q(1≤Q≤1500)。

之后的 Q 行,每行有两个整数 i 和 j(1≤i,jN),表示每天被翻转的路标的坐标。

【输出】

共 Q+1 行:第一行是初始的总价格,之后 Q 行依次是每次被翻转后的总价格。

【输入样例】

2
RR 1
DD 10
100 500
4
1 1
1 1
1 1
2 1

【输出样例】

602
701
602
701
1501

【代码详解】

USACO历年白银组真题解析 | 2023年1月Following Directions_第1张图片

#include 
using namespace std;
const int maxN = 1505;
char dir[maxN][maxN];
int n, sum[maxN][maxN], costR[maxN], costD[maxN];

int calc()
{
    int ans = 0;
    for (int r=1; r<=n; r++) {  // 遍历所有行
        ans += sum[r][n+1] * costR[r];  // 计算每行的价格,并进行累加
    }
    for (int c=1; c<=n; c++) {  // 遍历所有列
        ans += sum[n+1][c] * costD[c];  // 计算每列的价格,并累加
    }
    return ans;  // 返回总价格
}
int main()
{
    cin >> n;  // 输入n
    for (int r=1; r<=n; r++) {  // 输入所有字符
        for (int c=1; c<=n; c++) {
            cin >> dir[r][c];
        }
        cin >> costR[r];  // 每行最后一个数字
    }
    for (int c=1; c<=n; c++) {  /// 输入每列的价格
        cin >> costD[c];
    }
    for (int r=1; r<=n; r++) {  // 每个位置的sum[r][c]初始为1,用于后面递推
        for (int c=1; c<=n; c++) {
            sum[r][c] = 1;
        }
    }
    for (int r=1; r<=n; r++) {  // 进行递推
        for (int c=1; c<=n; c++) {
            if (dir[r][c]=='R') {  // 如果当前字符为'R'
                sum[r][c+1] += sum[r][c];  // 右边的sum[r][c]加上当前值
            }
            if (dir[r][c]=='D') {  // 如果当前字符为'D'
                sum[r+1][c] += sum[r][c];  // 下面的sum[r][c]加上当前值
            }
        }
    }
    cout << calc() << endl;  // 输出未翻转前的总价格
    int q;
    cin >> q;  // 输入q
    while (q--) {  // 遍历q次询问
        int r, c;
        cin >> r >> c;  // 输入被翻转的坐标
        int tmpR = r, tmpC = c, tmpSum = sum[r][c];  // 临时变量用来保存r、c和tmpSum,因为r和c要变化
        while (r<=n && c<=n) {  // 先从r->n和c->n,每个位置减掉tmpSum
            if (dir[r][c] == 'R') {  // 如果当前字符为'R'
                sum[r][++c] -= tmpSum;  // 则右边的sum[r][c]减去tmpSum
            } else {  // 如果当前字符为'D'
                sum[++r][c] -= tmpSum;  // 则下边的sum[r][c]减去tmpSum
            }
        }
        r = tmpR, c = tmpC;  // 还原r和c
        if (dir[r][c]=='R') {  // 设定翻转后的路标
            dir[r][c] = 'D';
        } else {
            dir[r][c] = 'R';
        }
        while (r<=n && c<=n) {  // 再次遍历r->c和c->n,每个位置再加回来tmpSum
            if (dir[r][c] == 'R') {  // 如果当前字符为'R'
                sum[r][++c] += tmpSum;  // 则右边的sum[r][c]减去tmpSum
            } else {  // 如果当前字符为'D'
                sum[++r][c] += tmpSum;  // 则下边的sum[r][c]减去tmpSum 
            }
        }
        cout << calc() << endl;  // 输出每轮调整后的总和
    }
    return 0;
}

【运行结果】

2
RR 1
DD 10
100 500
602
4
1 1
701
1 1
602
1 1
701
2 1
1501

你可能感兴趣的:(java,算法,数据结构)