2017-2018 ACM-ICPC Southeast USA Regional Contest (Div. 1)
前言
26号花四个小时练了一下,A了三题,太菜了QAQ
本文讲四题,剩下的题目如果能懂再说 _(:з」∠)_
关于训练
开头十多分钟,一直在看A、B题,直到看到有人A了i题,于是看了一下A掉这道签到题
转而看J题,感觉好像是DP,于是用DP思路写了一下AC
看到D题过的人比较多,于是看D,感觉很像POJ的滑雪,用DFS+DP交了一发TLE,想了一下好像复杂度的确爆炸,本来可以用线段树优化,但是担心爆内存,于是用了单调队列优化,结果写错了WA了一发 = =||,改完AC
然后就再也A不掉其他题目了 QAQ
Star Arrangements - Gym - 101617I
题意
给定星星数量,要求奇数行比偶数行摆放的数量一样或多1,求全部摆法
思路
签到题,不说了
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 10000 + 15;
const int inf = 0x3f3f3f3f;
typedef long long ll;
const ll MOD = 1000000007;
int main(){
int n;
while(~scanf("%d", &n)){
for(ll i = 2; i <= n && 2*i - 1 <= n; i++){
if(n%(2*i - 1) == 0 || n%(2*i - 1) == i){
printf("%lld %lld\n", i, i - 1);
}
if(n%(2*i) == 0 || n%(2*i) == i){
printf("%lld %lld\n", i, i);
}
}
}
}
Treasure Map Gym - 101617J
题意
给出n个点,m条边,每一条边需要花k天才能走完,第一天的时候这个人在点1,第i个点在第一天的时候有权值gi,每天减少di,最多减少到零,在不能相邻两天都待在同一个点的情况下,问这个人能得到的最大价值是多少
思路 —— DP
定义DP状态dp[u][j]
为点u在第j天能获得的最大价值,则
dp[v][j + w] = max(dp[u][j] + max(0, g[v] - d[v]*(j + w)))
答案就在其中取最大
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 1000 + 15;
const int inf = 0x3f3f3f3f;
typedef long long ll;
const ll MOD = 1000000007;
struct edge{
int v, nxt, w;
};
edge e[N << 1];
int head[N];
int tot;
int g[N], d[N];
ll dp[N][N];
inline void init(){
memset(head, -1, sizeof(head));
tot = 0;
}
inline void addEdge(int u, int v, int w){
e[tot] = edge{v, head[u], w};
head[u] = tot++;
}
int main(){
int n, m;
while(~scanf("%d%d", &n, &m)){
init();
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++){
scanf("%d%d", &g[i], &d[i]);
}
while(m--){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addEdge(u, v, w);
addEdge(v, u, w);
}
ll ans = g[1];
dp[1][0] = g[1];
for(int j = 0; j < N - 1; j++){
for(int u = 1; u <= n; u++){
if(dp[u][j]){
for(int i = head[u]; ~i; i = e[i].nxt){
int v = e[i].v;
int w = e[i].w;
if(j + w >= N) continue;
dp[v][j + w] = max(dp[v][j + w], dp[u][j] + max(0, g[v] - d[v]*(j + w)));
ans = max(ans, dp[v][j + w]);
}
}
}
}
printf("%lld\n", ans);
}
}
Jumping Haybales - Gym - 101617D
题意
给定一幅图,要求从左上角走到右下角,而且只能够往右或往下走,一次可以走k步,但是不能够落在‘#’的点中,问最少步数
思路 —— 单调队列 + DP
定义DP状态为dp[i][j]
为落到第(i,j)的最少步数,则
dp[i][j] = min(min(dp[i - k][j], dp[i - k + 1][j], ..., dp[i - 1][j]), min(dp[i][j - k], dp[i][j - k + 1], ..., dp[i][j - 1]))
但是要取出中间的最小值是比较麻烦的,如果选择用线段树的话容易爆内存,可以选择使用单调队列优化,毕竟这是定长RMQ问题了
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <deque>
using namespace std;
const int N = 2000 + 15;
const int inf = 0x3f3f3f3f;
typedef long long ll;
typedef pair<int, int> pii;
const ll MOD = 1000000007;
char G[N][N];
int d[N][N];
deque<pii> dqrow[N], dqcol[N];
int getD(int n, int k){
for(int i = 0; i < n; i++){
dqrow[i].clear();
dqcol[i].clear();
}
d[0][0] = 0;
dqrow[0].push_back(make_pair(0, 0));
dqcol[0].push_back(make_pair(0, 0));
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(i == 0 && j == 0) continue;
d[i][j] = inf;
while(!dqrow[i].empty() && dqrow[i].front().second < j - k) dqrow[i].pop_front();
while(!dqcol[j].empty() && dqcol[j].front().second < i - k) dqcol[j].pop_front();
if(G[i][j] != '#' && !dqrow[i].empty()) d[i][j] = min(d[i][j], dqrow[i].front().first + 1);
if(G[i][j] != '#' && !dqcol[j].empty()) d[i][j] = min(d[i][j], dqcol[j].front().first + 1);
while(!dqrow[i].empty() && dqrow[i].back().first > d[i][j]) dqrow[i].pop_back();
dqrow[i].push_back(make_pair(d[i][j], j));
while(!dqcol[j].empty() && dqcol[j].back().first > d[i][j]) dqcol[j].pop_back();
dqcol[j].push_back(make_pair(d[i][j], i));
}
}
return d[n - 1][n - 1];
}
int main(){
int n, k;
while(~scanf("%d%d", &n, &k)){
for(int i = 0; i < n; i++){
scanf("%s", G[i]);
}
int ans = getD(n, k);
printf("%d\n", ans == inf ? -1 : ans);
}
}
Security Badges - Gym - 101617H
题意
给定n个点m条边,通过每个点都有一个范围[l,r],问从源点到汇点有多少数字是可以通过的
思路 —— 离散化 + 暴力
看了一下题解 orz
把区间端点提出来离散化,然后直接枚举左右端点即可,因为经过离散化之后原来的区间变成了小区间,所以答案一定是某些区间的并集
另外为了方便合并,离散化时采用左闭右开的区间形式,枚举的时候右端减1即可
最后枚举区间,如果ok就累加区间长度作为答案
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000 + 15;
const int M = 5000 + 15;
const int inf = 0x3f3f3f3f;
struct edge{
int v, nxt, l, r;
};
edge e[M];
int head[N];
int tot;
int a[M << 1], pp;
bool used[N];
inline void init(){
memset(head, -1, sizeof(head));
tot = pp = 0;
}
inline void addEdge(int u, int v, int l, int r){
e[tot] = edge{v, head[u], l, r};
head[u] = tot++;
}
bool dfs(int u, int des, int l, int r){
if(u == des) return true;
used[u] = true;
for(int i = head[u]; ~i; i = e[i].nxt){
int v = e[i].v;
if(!used[v] && e[i].l <= l && r <= e[i].r && dfs(v, des, l, r)){
//used[u] = false;
return true;
}
}
//used[u] = false;
return false;
}
int main(){
int n, m, k;
while(~scanf("%d%d%d", &n, &m, &k)){
init();
int src, des;
scanf("%d%d", &src, &des);
while(m--){
int u, v, l, r;
scanf("%d%d%d%d", &u, &v, &l, &r);
addEdge(u, v, l, r);
a[pp++] = l;
a[pp++] = r + 1;
}
sort(a, a + pp);
pp = unique(a, a + pp) - a;
int ans = 0;
for(int i = 1; i < pp; i++){
memset(used, false, sizeof(used));
if(dfs(src, des, a[i - 1], a[i] - 1)){
ans += (a[i] - a[i - 1]);
}
}
printf("%d\n", ans);
}
}