codeforces的服务器在比赛时炸了,直接导致我心态爆炸,再加上其他各种倒霉原因,才A了两题。来愉快地写题解吧,话说现在在第二场之前写第一场的题解是什么鬼。。。总之先挖个坑吧
题目来源均为codeforces
Problem A Gym 100963B
对一个货币系统,给定货币的各种面额,判断对各种金额支付优先使用最大面额货币使得使用货币数目最小的贪心算法是否是最优解
考试时想多了,只是简单的dp
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAX_N 50 + 10
#define MAX_C 1000 + 10
int n;
int c[MAX_N];
int dp[2 * MAX_C];
int main() {
int kase = 1;
while (~scanf("%d", &n)) {
if (n == 0) break;
for (int i = 1;i <= n;i++)
scanf("%d", &c[i]);
if (c[1] != 1) {
printf("Case #%d: Cannot pay some amount\n", kase++);
continue;
}
fill(dp + 1, dp + 2 * c[n] + 1, 2 * MAX_C);
dp[0] = 0;
for (int i = 1;i <= 2 * c[n];i++)
for (int j = 1;j <= n;j++)
if (i >= c[j])
dp[i] = min(dp[i], dp[i - c[j]] + 1);
bool flag = true;
for (int i = 1;i <= 2 * c[n];i++) {
int ret = 0;
int t = i;
for (int j = n;j >= 1;j--)
if (t >= c[j]) {
ret += t / c[j];
t -= (t / c[j]) * c[j];
}
if (ret != dp[i]) {
flag = false;
break;
}
}
if (flag) printf("Case #%d: OK\n", kase++);
else printf("Case #%d: Cannot use greedy algorithm\n", kase++);
}
return 0;
}
Problem B Gym 100963C
简单的没有优先级的计算器,一步步计算R1 opt R2,得到最终结果,中间结果t<0 || t > 9999时属于越界,输出E。简单的模拟题,考试时队友翻译相当不准确,直接导致WA,后来舍友提起才发现没有优先级,但还是莫名其妙地WA。一直找不到原因,直到给R2也判越界才A掉。推测是R1*R2的结果溢出了,设计得long long都装不下(最长有80个字符)。所以说我还是太年轻了。。。
ps: 对有优先级的表达式该如何做呢?答案是对所有数存到数组里,遇到乘法或除法将两数相乘的结果存到第二个数,第一个数赋值为0,这样对连续的乘除也可以处理,遇到减法当作加上一个负数,将负数存到数组里。
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
using namespace std;
typedef long long ll;
int main() {
int r1, r2;
char r3;
char s[80];
while (~scanf("%s", s)) {
r1 = r2 = 0;
r3 = '!';
bool flag = true;
for (int i = 0;i < strlen(s);i++)
if (s[i] >= '0' && s[i] <= '9')
r2 = r2 * 10 + s[i] - '0';
else {
if (r3 == '+') r1 += r2;
else if (r3 == '-') r1 -= r2;
else if (r3 == '*') r1 *= r2;
else r1 = r2;
if (r1 < 0 || r1 >= 10000 || r2 < 0 || r2 >= 10000)
flag = false;
r3 = s[i];
r2 = 0;
if (r3 == '=') {
if (!flag) puts("E");
else printf("%d\n", r1);
}
}
}
return 0;
}
Problem C Gym100963F
N
N
N(
1
<
=
N
<
=
100
1<=N<=100
1<=N<=100)个城市,每个城市初始人数为
P
i
(
0
<
=
P
i
<
=
1
0
6
)
P_i(0<=P_i<=10^6)
Pi(0<=Pi<=106),每个城市可容纳人数为
K
i
K_i
Ki。城市之间存在M条道路,第i条道路连接着城市
A
i
A_i
Ai和
B
i
B_i
Bi,需要花费时间
D
i
(
1
<
=
D
i
<
=
10000
)
D_i(1<=D_i<=10000)
Di(1<=Di<=10000)天,
L
L
L(
1
<
=
L
<
=
10000
1<=L<=10000
1<=L<=10000)天后在各个城市里如果人数大于
K
i
(
0
<
=
K
i
<
=
1
0
6
)
K_i(0<=K_i<=10^6)
Ki(0<=Ki<=106)的人,那么超出
K
i
K_i
Ki的部分人会死去,问存活的最多人数。
一个网络流的简单变化情况。设置一个超级源点和超级汇点,将各个城市拆分为两个点,源点和其中一个点i连边,权值为
P
i
P_i
Pi,另一个点N + i与汇点相连,权值为Ki。跑一遍Floyd得到每个城市i与其能到达的城市j,将i与N + j,j与N + i连边,权值为
m
a
x
(
P
i
)
max(P_i)
max(Pi),这样就建好图了。Floyd的复杂度为
O
(
N
3
O(N^3
O(N3),网络流用Ford-Fulkerson算法复杂度
O
(
F
∣
E
∣
O(F|E|
O(F∣E∣),用Dinic算法复杂度为
O
(
∣
E
∣
∣
V
∣
2
O(|E||V|^2
O(∣E∣∣V∣2),用Dinic更优。
网络流很多问题属于建图问题,图建好了问题也就解决了。
ps:注意网络流中点数为
2
∗
N
+
2
2*N+2
2∗N+2,以及INF的大小
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
#define MAX_V 200 + 10
#define INF 2000000
struct edge { int to, cap, rev; };
int T;
vector<edge> G[MAX_V];
int level[MAX_V];
int iter[MAX_V];
void add_edge(int from, int to, int cap) {
G[from].push_back((edge){to, cap, G[to].size()});
G[to].push_back((edge){from, 0, G[from].size() - 1});
}
void bfs(int s) {
memset(level, -1, sizeof(level));
queue<int> que;
level[s] = 0;
que.push(s);
while (!que.empty()) {
int v = que.front();que.pop();
for (int i = 0;i < G[v].size();i++) {
edge &e = G[v][i];
if (e.cap > 0 && level[e.to] < 0) {
level[e.to] = level[v] + 1;
que.push(e.to);
}
}
}
}
int dfs(int v, int t, int f) {
if (v == t) return f;
for (int &i = iter[v];i < G[v].size();i++) {
edge &e = G[v][i];
if (e.cap > 0 && level[v] < level[e.to]) {
int d = dfs(e.to, t, min(f, e.cap));
if (d > 0) {
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow(int s, int t) {
int flow = 0;
for(;;) {
bfs(s);
if (level[t] < 0) return flow;
memset(iter, 0, sizeof(iter));
int f;
while ((f = dfs(s, t, INF)) > 0) {
flow += f;
}
}
}
int d[MAX_V][MAX_V];
int N, M, L;
int P[MAX_V], K[MAX_V];
int main() {
while(scanf("%d%d%d", &N, &M, &L) != EOF) {
for (int i = 1;i <= N;i++)
for (int j = 1;j <= N;j++)
if (i == j) d[i][j] = 0;
else d[i][j] = INF;
for (int i = 0;i <= 201;i++)
G[i].clear();
for (int i = 0;i < M;i++) {
int A, B, D;
scanf("%d%d%d", &A, &B, &D);
d[A][B] = d[B][A] = D;
}
for (int k = 1;k <= N;k++)
for (int i = 1;i <= N;i++)
for (int j = 1;j <= N;j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
for (int i = 1;i <= N;i++)
scanf("%d", &P[i]);
for (int i = 1;i <= N;i++)
scanf("%d", &K[i]);
for (int i = 1;i <= N;i++)
for (int j = 1;j <= N;j++)
if (d[i][j] < L) {
add_edge(i, N + j, 1000000);
add_edge(j, N + i, 1000000);
}
for (int i = 1;i <= N;i++) {
add_edge(0, i, P[i]);
add_edge(N + i, 2 * N + 1, K[i]);
}
printf("%d\n", max_flow(0, 2 * N + 1));
}
return 0;
}
Problem E Gym 100963J
对
a
x
+
b
=
c
y
+
d
=
k
ax+b=cy+d=k
ax+b=cy+d=k,求
k
k
k
移项一下就是裸extgcd
Problem F Gym 100837A
给定两个正整数,分解质因数用最大质因数减去其他不重复质因数,比较大小
#include <cstdio>
#include <algorithm>
using namespace std;
int main() {
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int a, b;
int max1, max2;
int res1, res2;
res1 = res2 = 0;
scanf("%d%d", &a, &b);
for (int i = 2;i <= a;i++)
if (a % i == 0) {
res1 += i;
max1 = i;
while (a % i == 0) a /= i;
}
for (int i = 2;i <= b;i++)
if (b % i == 0) {
res2 += i;
max2 = i;
while (b % i == 0) b /= i;
}
res1 = max1 - (res1 - max1);
res2 = max2 - (res2 - max2);
//printf("%d\n", res1);
if (res1 > res2) printf("a");
else printf("b");
return 0;
}
Problem G Gym 100837B
给定分子分母,求循环节长度,和循环部分之前的小数位数。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a, b;
int k, ans1, ans2;
int flag[10000000], flag2[10000000];
int main() {
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
scanf("%d%d", &a, &b);
int m = a % b;
if (m == 0) {
puts("0 0");
return 0;
}
memset(flag, -1, 10000000);
while (true) {
int m1 = m;
m = (m * 10) % b;
if (m == 0) {
ans1 = ++k, ans2 = 0;
break;
}
if(flag[m] >= 0 && flag2[m] == m1) {
ans1 = flag[m], ans2 = k - flag[m];
break;
}
flag[m] = k, flag2[m] = m1;
k++;
}
printf("%d %d", ans1, ans2);
return 0;
}
Problem H
有一座山,山是由一系列首尾相连的直线组成的,给你直线的端点。你要从山的这头爬到山的那头。你可以爬山,在爬坡时也可以选择挖水平的洞穿过山。给定爬山和挖洞的速度,求所需的最短时间。
可以证明要选择挖洞的话起点或终点必是直线的端点(虽然没证)。那么就在每个可以挖洞的点挖洞,得到新点和新边,跑一遍最短路就解决了。
本题精度问题不大,开double就能解决
#include <cstdio>
#include <utility>
#include <queue>
#include <cstring>
#include <list>
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
#define MAX_V 2000 + 10
#define MAX_E 3000 + 10
#define INF 0x3f3f3f
struct edge { int to; double cost; };
typedef pair<int, int> P;
int V;
vector<edge> G[MAX_E];
double d[MAX_V];
void dijkstra(int s) {
priority_queue<P, vector<P>, greater<P> > que;
fill(d, d + V + 1, INF);
d[s] = 0;
que.push(P(0, s));
while (!que.empty()) {
P p = que.top();que.pop();
int v = p.second;
if (d[v] < p.first) continue;
for (int i = 0;i < G[v].size();i++) {
edge e = G[v][i];
if (d[e.to] > d[v] + e.cost) {
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
}
int n;
double vw, vc;
double x[MAX_V], y[MAX_V];
bool l[MAX_V];
int v[MAX_V];
int cnt;
double dist(double x1, double y1, double x2, double y2) {
return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
int main() {
freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);
scanf("%d", &n);
scanf("%lf%lf", &vw, &vc);
for (int i = 1;i <= n;i++)
scanf("%lf%lf", &x[i], &y[i]);
for (int i = 2;i <= n;i++) {
double d = dist(x[i - 1], y[i - 1], x[i], y[i]);
G[i - 1].push_back((edge){i, d * vc});
}
V = n;
int i = 2;
l[1] = true;
while (i <= n) {
for (int iter = 1;iter < i;iter++) {
if (l[iter] && y[iter] >= y[i]) {
if (y[iter] < y[i - 1]) {
double k = (x[i] - x[i - 1]) / (y[i] - y[i - 1]);
double b = x[i] - k * y[i];
double x1 = k * y[iter] + b;
G[iter].push_back((edge){++V, (x1 - x[iter]) * vw});
G[V].push_back((edge){i, dist(x1, y[iter], x[i], y[i]) * vc});
}
l[iter] = false;
}
}
l[i++] = true;
}
memset(l, 0, sizeof(l));
i = n - 1;
l[n] = true;
while (i >= 1) {
for (int iter = n;iter > i;iter--) {
if (l[iter] && y[iter] > y[i]) {
if (y[iter] < y[i + 1]) {
double k = (x[i + 1] - x[i]) / (y[i + 1] - y[i]);
double b = x[i] - k * y[i];
double x1 = k * y[iter] + b;
G[++V].push_back((edge){iter, (x[iter] - x1) * vw});
G[i].push_back((edge){V, dist(x1, y[iter], x[i], y[i]) * vc});
}
l[iter] = false;
}
}
l[i--] = true;
}
dijkstra(1);
printf("%.6f\n", d[n] / (vc * vw));
return 0;
}