【解题报告】Codeforces Round #374 (Div. 2)

题目链接


A.One-dimensional Japanese Crossword(Codeforces 721A)

思路

本题是让我们统计题给字符串中连续的‘ B 块’的块数,及每块的长度。可以将字符串中的‘ W ’全部改成空格,再制成字符串流,然后从字符串流中边输入代表‘ B 块’的子串边统计子串长度就能得到答案了。

代码

#include 
using namespace std;

int n;
string s;
vector <int> ans;

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> s;
    for(int i = 0; i < s.size(); i++) {
        if(s[i] == 'W') {
            s[i] = ' ';
        }
    }
    stringstream ss(s);
    while(ss >> s) {
        ans.push_back(s.size());
    }
    cout << ans.size() << endl;
    for(int i = 0; i < ans.size(); i++) {
        cout << ans[i] << ' ';
    }
    return 0;
}

B. Passwords(Codeforces 721B)

思路

由于密码是按照长度顺序输入,而同长度的密码随机输入。
因此最好的情况是输入完长度小于真实密码的密码之后(设其共有 sum 个)立即输入到真实密码。时间 = 罚时 + 真实时间 =sum/k×5+sum+1
最坏的情况是输入完长度小于真实密码的密码之后将与真实密码长度相同的其它密码(设其有 num1 个)全部输完才输入真实密码,时间 = 罚时 + 真实时间 =(sum+num1)/k×5+sum+num

代码

#include 
using namespace std;

const int maxn = 200;
int n, k, a, b, len, sum, num, cnt[maxn];
string s;

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> k;
    for(int i = 0; i < n; i++) {
        cin >> s;
        cnt[s.size()]++;
    }
    cin >> s;
    len = s.size();
    for(int i = 1; i < len; i++) {
        sum += cnt[i];
    }
    num = cnt[len];
    a = sum / k * 5 + sum + 1;
    b = (sum + num - 1) / k * 5 + sum + num;
    cout << a << ' ' << b << endl;
    return 0;
}

C.Journey(Codeforces 721C)

思路

因为本题的图是个有向无环联通图,又需要在这个图上求某个最大值。所以自然想到用动态规划来解决。但是因为动态规划需要一个“序”,而这个图是无序的,因此想到从点 1 开始对这张图做一个拓扑排序,然后做动态规划。然而在拓扑排序的过程中动态规划可以同时进行。
定义 d[i][j] 为从 i 走到 n ,在经过 j 个节点的情况下花费的最小时间。其中 d[u][j]=min(d[u][j],d[v][j1]+w) ,其中 v 为拓扑序中 u 的下一个点,另外 w 为边权(时间)。对于一个给定的 u j v 要遍取所有的情况才能更新 d[u][j]
为了按照主人公行走的顺序输出点的编号,在更新 d[u][j] 的时候要记录在此情况下 u 的下一个节点,存储在 x[u][j] 中。为了将时间限制在 T 秒内,需要将 d 数组的所有值初始化为 T+1 ,再令 d[n][1]=0
在这个算法下,所有的点都只被访问一次,被访问的点都只会做 O(n) 的动态规划,所有的边也只被访问一次,因此时间复杂度为 O(n2+m)

代码


#include 
using namespace std;

typedef pair <int, int> p;
const int maxn = 5010;
bool vis[maxn];
int n, m, T, u, v, w, ans, cur, x[maxn][maxn], d[maxn][maxn];
vector 

G[maxn]; // 在拓扑排序的同时动态规划 void dfs(int u) { vis[u] = true; for(int i = 0; i < G[u].size(); i++) { p& node = G[u][i]; int v = node.first; int w = node.second; if(vis[v] == false) { dfs(v); } for(int j = 2; j <= n; j++) { if(d[u][j] > d[v][j-1] + w) { // 更新数组 d[u][j] = d[v][j-1] + w; // 记录路径 x[u][j] = v; } } } } int main() { scanf("%d%d%d", &n, &m, &T); while(m--) { scanf("%d%d%d", &u, &v, &w); G[u].push_back(p(v, w)); } // 初始化 for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { d[i][j] = T + 1; } } d[n][1] = 0; // 拓扑排序 dfs(1); for(int j = n; j >= 1; j--) { if(d[1][j] <= T) { ans = j; break; } } printf("%d\n", ans); // 按行走顺序输出 cur = 1; for(int i = ans; i >= 1; i--) { printf("%d ", cur); cur = x[cur][i]; } return 0; }


D.Maxim and Array(Codeforces 721D)

思路

序列的积由两个值唯一决定,一个是积的负号,另一个是积的绝对值。
当积的符号为正的时候,只要将序列中绝对值最小的元素不断减小(直到积的符号为负)就能够减小积的大小。
当积的符号为负的时候,只要将积的绝对值增大就能减小积的大小。为了让积的绝对值增大,可以找到序列中绝对值最小的元素,增加其绝对值即可。
那么我们可以在模拟k减小的同时维护积的符号 sign ,并根据其值来执行相应的操作。为了动态获得当前状态下序列中绝对值最小的元素。我们需要用一个堆来维护序列中每个元素的绝对值。这样总的复杂度是 O(nlogn)

代码

#include 
using namespace std;

typedef long long ll;
typedef pair int> p;
const int maxn = 2e5 + 5;
// sign的初始值为0,表示积为非负数
int n, k, x, v, d, sign, flag;
ll a[maxn];
priority_queue < p, vector

, greater

> pq; int main() { scanf("%d%d%d", &n, &k, &x); for(int i = 0; i < n; i++) { scanf("%I64d", &a[i]); // 将绝对值插入堆中,附加位置信息 pq.push(p(abs(a[i]), i)); // 当a[i]为负数时修改积的符号 sign ^= (a[i] < 0); } // 模拟数组的减小过程 while(k--) { p tmp = pq.top(); pq.pop(); v = tmp.first; d = tmp.second; // 记录修改之前的符号 flag = (a[d] < 0); // 修改序列的元素的值 a[d] += (sign ^ flag ? 1 : -1) * x; // 若符号改变则修改sign sign ^= flag ^ (a[d] < 0); pq.push(p(abs(a[d]), d)); } for(int i = 0; i < n; i++) { printf("%I64d ", a[i]); } return 0; }


(其它题目略)

你可能感兴趣的:(Codeforces)