2017ACM省赛选拔赛题解
Posted Flowersea
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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省赛选拔赛题解的主要内容,如果未能解决你的问题,请参考以下文章