2017ACM省赛选拔赛题解

时间:2022-10-26 13:50:24

Problem A: 聪明的田鼠

题解:

dp[k][i]表示走了k步,且在第i行的最大值

最后的结果就是走了n+m-2步,且在第n行的值

代码:

 1 #include <map>
2 #include <set>
3 #include <cmath>
4 #include <queue>
5 #include <stack>
6 #include <cstdio>
7 #include <string>
8 #include <vector>
9 #include <cstdlib>
10 #include <cstring>
11 #include <sstream>
12 #include <iostream>
13 #include <algorithm>
14 #include <functional>
15 using namespace std;
16 #define rep(i,a,n) for (int i=a;i<n;i++)
17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
18 #define pb push_back
19 #define mp make_pair
20 #define all(x) (x).begin(),(x).end()
21 #define SZ(x) ((int)(x).size())
22 typedef vector<int> VI;
23 typedef long long ll;
24 typedef pair<int, int> PII;
25 const ll MOD = 1e9 + 7;
26 const int INF = 0x3f3f3f3f;
27 const double EPS = 1e-10;
28 const double PI = acos(-1.0);
29 const int MAXN = 60;
30 // head
31
32 int n, m;
33 int a[MAXN][MAXN];
34 int dp[MAXN * 2][MAXN];
35
36 int main() {
37 cin >> n >> m;
38 rep(i, 1, n + 1) rep(j, 1, m + 1) cin >> a[i][j];
39 dp[0][1] = a[1][1];
40 rep(k, 1, n + m - 1) rep(i, max(1, k + 2 - m), n + 1) if (i <= k + 1)
41 dp[k][i] = max(dp[k - 1][i], dp[k - 1][i - 1]) + a[i][k + 2 - i];
42 cout << dp[n + m - 2][n] << endl;
43 return 0;
44 }

Problem B: 软件安装

题解:

线段树区间更新,区间查询

代码:

  1 #include <map>
2 #include <set>
3 #include <cmath>
4 #include <queue>
5 #include <stack>
6 #include <cstdio>
7 #include <string>
8 #include <vector>
9 #include <cstdlib>
10 #include <cstring>
11 #include <sstream>
12 #include <iostream>
13 #include <algorithm>
14 #include <functional>
15 using namespace std;
16 #define rep(i,a,n) for (int i=a;i<n;i++)
17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
18 #define all(x) (x).begin(),(x).end()
19 #define pb push_back
20 #define mp make_pair
21 #define lson l,m,rt<<1
22 #define rson m+1,r,rt<<1|1
23 typedef long long ll;
24 typedef vector<int> VI;
25 typedef pair<int, int> PII;
26 const ll MOD = 1e9 + 7;
27 const int INF = 0x3f3f3f3f;
28 const int MAXN = 5e4 + 7;
29 // head
30
31 int cover[MAXN << 2];
32 int lsum[MAXN << 2], rsum[MAXN << 2], msum[MAXN << 2];
33
34 void pushup(int rt, int m) {
35 lsum[rt] = lsum[rt << 1], rsum[rt] = rsum[rt << 1 | 1];
36 if (lsum[rt << 1] == m - (m >> 1)) lsum[rt] += lsum[rt << 1 | 1];
37 if (rsum[rt << 1 | 1] == m >> 1) rsum[rt] += rsum[rt << 1];
38 msum[rt] = max(max(msum[rt << 1], msum[rt << 1 | 1]), rsum[rt << 1] + lsum[rt << 1 | 1]);
39 }
40
41 void pushdown(int rt, int m) {
42 if (cover[rt] != -1) {
43 cover[rt << 1] = cover[rt << 1 | 1] = cover[rt];
44 lsum[rt << 1] = rsum[rt << 1] = msum[rt << 1] = (cover[rt] ? 0 : m - (m >> 1));
45 lsum[rt << 1 | 1] = rsum[rt << 1 | 1] = msum[rt << 1 | 1] = (cover[rt] ? 0 : m >> 1);
46 cover[rt] = -1;
47 }
48 }
49
50 void build(int l, int r, int rt) {
51 cover[rt] = -1;
52 lsum[rt] = rsum[rt] = msum[rt] = r - l + 1;
53 if (l == r) return;
54 int m = (l + r) >> 1;
55 build(lson);
56 build(rson);
57 }
58
59 void update(int L, int R, int x, int l, int r, int rt) {
60 if (L <= l && r <= R) {
61 cover[rt] = x;
62 lsum[rt] = rsum[rt] = msum[rt] = (x ? 0 : r - l + 1);
63 return;
64 }
65 pushdown(rt, r - l + 1);
66 int m = (l + r) >> 1;
67 if (L <= m) update(L, R, x, lson);
68 if (R > m) update(L, R, x, rson);
69 pushup(rt, r - l + 1);
70 }
71
72 int query(int x, int l, int r, int rt) {
73 if (l == r) return l;
74 pushdown(rt, r - l + 1);
75 int m = (l + r) >> 1;
76 if (msum[rt << 1] >= x) return query(x, lson);
77 else if (rsum[rt << 1] + lsum[rt << 1 | 1] >= x) return m - rsum[rt << 1] + 1;
78 else query(x, rson);
79 }
80
81 int main() {
82 int n, m;
83 cin >> n >> m;
84 build(1, n, 1);
85 while (m--) {
86 int a, b, c;
87 scanf("%d", &a);
88 if (a == 1) {
89 scanf("%d", &b);
90 if (msum[1] < b) {
91 puts("0");
92 continue;
93 }
94 int ans = query(b, 1, n, 1);
95 printf("%d\n", ans);
96 update(ans, ans + b - 1, 1, 1, n, 1);
97 }
98 else {
99 scanf("%d%d",&b, &c);
100 update(b, b + c - 1, 0, 1, n, 1);
101 }
102 }
103 return 0;
104 }

 

Problem C: V型积木

题解:

最长上升子序列,枚举每个最为最低点,分别求左右两边的LIS,复杂度n³

不过学长说可以用树状数组,nlogn就能解决,万能的树状数组。。

代码:

 1 #include <map>
2 #include <set>
3 #include <cmath>
4 #include <queue>
5 #include <stack>
6 #include <cstdio>
7 #include <string>
8 #include <vector>
9 #include <cstdlib>
10 #include <cstring>
11 #include <sstream>
12 #include <iostream>
13 #include <algorithm>
14 #include <functional>
15 using namespace std;
16 #define rep(i,a,n) for (int i=a;i<n;i++)
17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
18 #define pb push_back
19 #define mp make_pair
20 #define all(x) (x).begin(),(x).end()
21 #define SZ(x) ((int)(x).size())
22 typedef vector<int> VI;
23 typedef long long ll;
24 typedef pair<int, int> PII;
25 const ll MOD = 1e9 + 7;
26 const int INF = 0x3f3f3f3f;
27 const double EPS = 1e-10;
28 const double PI = acos(-1.0);
29 const int MAXN = 110;
30 // head
31
32 int n;
33 int a[MAXN];
34 int dp[MAXN];
35
36 int main() {
37 int T;
38 cin >> T;
39 while (T--) {
40 cin >> n;
41 rep(i, 0, n) scanf("%d", a + i);
42 int ans = 0;
43 rep(k, 0, n) {
44 memset(dp, 0, sizeof(dp));
45 int sum = 0, t = 0;
46 per(i, 0, k) rep(j, i + 1, k + 1) if (a[i] > a[j])
47 dp[i] = max(dp[i], dp[j] + 1), t = max(t, dp[i]);
48 if (t == 0) continue;
49 sum += t;
50 t = 0;
51 rep(i, k + 1, n) rep(j, k, i) if (a[i] > a[j])
52 dp[i] = max(dp[i], dp[j] + 1), t = max(t, dp[i]);
53 if (t == 0) continue;
54 sum += t;
55 ans = max(ans, sum + 1);
56 }
57 if (ans == 0) cout << "No Solution" << endl;
58 else cout << n - ans << endl;
59 }
60 return 0;
61 }

 

Problem D: 最佳地址

题解:

最小费用流,建立一个超级源点s和超级汇点t

coal0表示原有的发电厂,coal1表示当前要新建的发电厂

从s到coal0连一条流量为b,费用为0的边

从s到coal1连一条流量为sum-b,费用为0的边  因为题目说了全部供应,所以剩下的所有煤矿都要供应到新发电厂

然后coal0和coal1都分别和每个煤矿相连,流量就是煤矿的产量a[i],费用就是c[0][i]和c[1][i]

最后每个煤矿再连接到t,流量为a[i],费用为0

然后跑一下最小费用流就可以了

点的标号分别为0-m-1为煤矿   m为coal0   m+1为coal1   m+2为s   m+3为t   一共m+4个点

代码:

  1 #include <map>
2 #include <set>
3 #include <cmath>
4 #include <queue>
5 #include <stack>
6 #include <cstdio>
7 #include <string>
8 #include <vector>
9 #include <cstdlib>
10 #include <cstring>
11 #include <sstream>
12 #include <iostream>
13 #include <algorithm>
14 #include <functional>
15 using namespace std;
16 #define rep(i,a,n) for (int i=a;i<n;i++)
17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
18 #define pb push_back
19 #define mp make_pair
20 #define all(x) (x).begin(),(x).end()
21 #define SZ(x) ((int)(x).size())
22 typedef vector<int> VI;
23 typedef long long ll;
24 typedef pair<int, int> PII;
25 const ll MOD = 1e9 + 7;
26 const int INF = 0x3f3f3f3f;
27 const double EPS = 1e-10;
28 const double PI = acos(-1.0);
29 const int MAXN = 110; //看了测试数据,m和n最大不超过100
30 // head
31
32 int n, b, m, H;
33 int a[MAXN];
34 int h[MAXN];
35 int c[MAXN][MAXN];
36
37 struct edge { int to, cap, cost, rev; };
38 const int MAX_V = 1e4 + 7;
39 int V;
40 vector<edge> G[MAX_V];
41 int dist[MAX_V];
42 int prevv[MAX_V], preve[MAX_V];
43
44 void add_edge(int from, int to, int cap, int cost) {
45 G[from].pb(edge{ to,cap,cost,(int)G[to].size() });
46 G[to].pb(edge{ from,0,-cost,(int)G[from].size() - 1 });
47 }
48
49 int min_cost_flow(int s, int t, int f) {
50 int res = 0;
51 while (f > 0) {
52 memset(dist, 0x3f, sizeof(dist));
53 dist[s] = 0;
54 bool update = true;
55 while (update) {
56 update = false;
57 rep(v, 0, V) {
58 if (dist[v] == INF) continue;
59 rep(i, 0, G[v].size()) {
60 edge &e = G[v][i];
61 if (e.cap > 0 && dist[e.to] > dist[v] + e.cost) {
62 dist[e.to] = dist[v] + e.cost;
63 prevv[e.to] = v;
64 preve[e.to] = i;
65 update = true;
66 }
67 }
68 }
69 }
70 if (dist[t] == INF) return -1;
71 int d = f;
72 for (int v = t; v != s; v = prevv[v]) d = min(d, G[prevv[v]][preve[v]].cap);
73 f -= d;
74 res += d*dist[t];
75 for (int v = t; v != s; v = prevv[v]) {
76 edge &e = G[prevv[v]][preve[v]];
77 e.cap -= d;
78 G[v][e.rev].cap += d;
79 }
80 }
81 return res;
82 }
83
84 int main() {
85 int T;
86 cin >> T;
87 while (T--) {
88 cin >> m >> b >> H >> n;
89 int sum = 0;
90 rep(i, 0, m) scanf("%d", a + i), sum += a[i];
91 rep(i, 0, n) scanf("%d", h + i);
92 rep(i, 0, m) scanf("%d", &c[0][i]);
93 PII ans = mp(0, INF);
94 rep(k, 0, n) {
95 rep(i, 0, MAX_V) G[i].clear();
96 rep(i, 0, m) scanf("%d", &c[1][i]);
97 int coal0 = m, coal1 = m + 1, s = m + 2, t = m + 3;
98 V = m + 4;
99 add_edge(s, coal0, b, 0);
100 add_edge(s, coal1, sum - b, 0);
101 rep(i, 0, m) {
102 add_edge(coal0, i, a[i], c[0][i]);
103 add_edge(coal1, i, a[i], c[1][i]);
104 add_edge(i, t, a[i], 0);
105 }
106 int mcf = min_cost_flow(s, t, sum) + H + h[k];
107 if (mcf < ans.second) ans.second = mcf, ans.first = k;
108 }
109 cout << ans.first + 1 << ' ' << ans.second << endl;
110 }
111 return 0;
112 }

Problem E: 寻宝

题解:

从0点(题目中的1,习惯从0开始,所以全都-1计算)跑一下改进版dijkstra

当然求的不是最短路,d[i]表示从0到i的所有路线中 每条路线中的权重最小的值  的最大值

这个只要稍微修改一下就好了 

c[i]存的就是有c[i]个宝藏地点 可以携带i个宝藏到达那里,因为最多有k个宝藏,所以超过k的也按k算

最后贪心模拟一下就好了

代码:

 1 #include <map>
2 #include <set>
3 #include <cmath>
4 #include <queue>
5 #include <stack>
6 #include <cstdio>
7 #include <string>
8 #include <vector>
9 #include <cstdlib>
10 #include <cstring>
11 #include <sstream>
12 #include <iostream>
13 #include <algorithm>
14 #include <functional>
15 using namespace std;
16 #define rep(i,a,n) for (int i=a;i<n;i++)
17 #define per(i,a,n) for (int i=n-1;i>=a;i--)
18 #define pb push_back
19 #define mp make_pair
20 #define all(x) (x).begin(),(x).end()
21 #define SZ(x) ((int)(x).size())
22 typedef vector<int> VI;
23 typedef long long ll;
24 typedef pair<int, int> PII;
25 const ll MOD = 1e9 + 7;
26 const int INF = 0x3f3f3f3f;
27 const double EPS = 1e-10;
28 const double PI = acos(-1.0);
29 const int MAXN = 8010;
30 // head
31
32 const int MAX_V = MAXN;
33 struct edge { int to, cost; };
34 vector<edge> G[MAX_V];
35 int d[MAX_V];
36 int V;
37
38 void dijkstra() {
39 priority_queue<PII, vector<PII>, greater<PII> > que;
40 d[0] = INF;
41 que.push(PII(INF, 0));
42
43 while (!que.empty()) {
44 PII p = que.top(); que.pop();
45 int v = p.second;
46 if (d[v] > p.first) continue;
47 rep(i, 0, G[v].size()) {
48 edge e = G[v][i];
49 if (d[e.to] < min(d[v], e.cost)) {
50 d[e.to] = min(d[v], e.cost);
51 que.push(PII(d[e.to], e.to));
52 }
53 }
54 }
55 }
56
57 int n, m, k, w;
58 int a[MAXN], c[MAXN];
59
60 int main() {
61 scanf("%d%d%d%d", &n, &m, &k, &w);
62 V = n;
63 rep(i, 0, k) {
64 int t;
65 scanf("%d", &t);
66 a[--t] = 1;
67 }
68 while (m--) {
69 int x, y, z;
70 scanf("%d%d%d", &x, &y, &z);
71 x--, y--;
72 G[x].pb(edge{ y,z });
73 G[y].pb(edge{ x,z });
74 }
75 dijkstra();
76 rep(i, 0, n) if (a[i]) c[min(d[i] / w, k)]++;
77 int sum = 0, sur = 0, ans = k;
78 per(i, 1, k + 1) {
79 if (c[i]) sur += c[i] - 1;
80 else if (sur) sur--;
81 else ans--;
82 }
83 cout << ans << endl;
84 return 0;
85 }