2017ACM省赛选拔赛题解

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
", 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 }
原文地址:https://www.cnblogs.com/baocong/p/6690546.html