首页 > 代码库 > 2017ACM省赛选拔赛题解

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<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int N=5e5+7;
 5 int read(){
 6     int x=0,f=1;char ch=getchar();
 7     while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
 8     while(ch>=0&&ch<=9)x=x*10+ch-0,ch=getchar();
 9     return x*f; 
10 }
11 #define lson (rt<<1)
12 #define rson (rt<<1|1)
13 int msum[N<<2],lsum[N<<2],rsum[N<<2],len[N<<2],tag[N<<1],n,m;
14 void pushup(int rt){
15     lsum[rt]=lsum[lson];
16     rsum[rt]=rsum[rson];
17     if(lsum[lson]==len[lson]){lsum[rt]=lsum[lson]+lsum[rson];}
18     if(rsum[rson]==len[rson]){rsum[rt]=rsum[rson]+rsum[lson];}
19     msum[rt]=max(max(msum[lson],msum[rson]),max(lsum[rt],rsum[rt]));
20     msum[rt]=max(msum[rt],rsum[lson]+lsum[rson]);
21 }
22 void pushdown(int rt){
23     int t=tag[rt];
24     if(t!=-1){
25         msum[lson]=lsum[lson]=rsum[lson]=len[lson]*t;
26         msum[rson]=lsum[rson]=rsum[rson]=len[rson]*t;
27         tag[rson]=tag[lson]=t;
28         tag[rt]=-1;
29     }
30 }
31 void build(int rt,int l,int r){
32     if(l>r)return;
33     msum[rt]=lsum[rt]=rsum[rt]=len[rt]=r-l+1;tag[rt]=-1;
34     if(l==r)return;
35     int mid=l+r>>1;
36     build(lson,l,mid);build(rson,mid+1,r);
37 }
38 void modify(int rt,int l,int r,int a,int b,int val){
39     if(a<=l&&r<=b){msum[rt]=rsum[rt]=lsum[rt]=val*len[rt];tag[rt]=val;return;}
40     pushdown(rt);
41     int mid=l+r>>1;
42     if(a<=mid)modify(lson,l,mid,a,b,val);
43     if(b>mid)modify(rson,mid+1,r,a,b,val);
44     pushup(rt);
45 }
46 int query(int rt,int l,int r,int k){
47     pushdown(rt);
48     int mid=l+r>>1;
49     if(msum[lson]>=k)return query(lson,l,mid,k);
50     else if(rsum[lson]+lsum[rson]>=k)return mid-rsum[lson]+1;
51     else return query(rson,mid+1,r,k);
52 }
53 int main(){
54     n=read();m=read();
55     build(1,1,n);
56     while(m--){
57         int op=read(),a=read(),b;
58         if(op==1){
59             if(msum[1]<a)puts("0");
60             else{
61                 int p=query(1,1,n,a);
62                 printf("%d\n",p);
63                 modify(1,1,n,p,p+a-1,0);
64             }
65         }else{
66             b=read();
67             modify(1,1,n,a,a+b-1,1);
68         }
69     }
70 }

Problem C: V型积木

题解:

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

不过学长说可以用树状数组,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 }

 

2017ACM省赛选拔赛题解