10.19 模拟赛题解报告

写在前面

得分情况:\(100 + 100 + 10\)

(次短路板子忘了怎么写的大蒟蒻 = =

T1

直接贪心看每一位 \(0\) 多还是 \(1\) 多就好了。

/*
work by: Ariel_
Knowledge:
Time:
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int MAXN = 1e3 + 5;
int read() {
  int x = 0, f = 1; char c = getchar();
  while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
  return x * f;
}
int n;
char s[MAXN];
struct node{int cnt_0, cnt_1;}cnt[MAXN];
int main(){
  //freopen("curse.in", "r", stdin);
  //freopen("curse.out", "w", stdout);
  n = read();
  for (int i = 1; i <= n; i++) {
  	 scanf("%s", s + 1);
  	 int len = strlen(s + 1);
  	 for (int j = 1; j <= len; j++) {
  	    if(s[j] == '0') cnt[j].cnt_0++;
		else cnt[j].cnt_1++;	 
	 }
  }
  int len = strlen(s + 1);
  for (int i = 1; i <= len; i++) {
  	if(cnt[i].cnt_0 >= cnt[i].cnt_1) putchar('0');
	else putchar('1');
  }
  return 0;
}

T2

很显然要二分这个 \(L\),关键是如何写 \(check\)。

首先发现 \(G\) 和 \(R\) 的范围很大,但 \(n\) 的范围很小。

再想一下,其实当 \(G + R \geq n\) 的时候,\(L\) 就可以设为 \(1\),这样就将 \(G, R\) 的范围缩小到了 \(2000\) 以内。

考虑 \(dp\) 判断:

算法一

设 \(f_{i, j}\) 用了 \(i\) 次红光,用了 \(j\) 次绿光向右扩展到的最远的法坛。

预处理 \(p_i\) 表示从 \(i\) 点用红光能向右能扩展到最远的是哪个法坛,\(q_i\)表示从 \(i\) 点用绿光向右能扩展到最远的是哪个法坛。

\(f_{i, j} = \max\{p[f_{i - 1, j}+1], q[f_{i, j - 1}+1]\}\)

code

/*
work by: Ariel_
Knowledge:
Time:
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;
int read() {
  int x = 0, f = 1; char c = getchar();
  while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
  return x * f;
}
int n, R, G, a[MAXN], Ans, f[2010][2010];
bool Check(int L) {
   for (int i = 1; i <= n; i++) 
   	 for (int j = 0; j <= R; j++) f[i][j] = INF;
   int l1 = 0, l2 = 0;
   f[0][0] = 0;
   for (int i = 1; i <= n; i++){
      while(l1 < n && a[l1 + 1] + L <= a[i]) l1++;
      while(l2 < n && a[l2 + 1] + 2 * L <= a[i]) l2++;
      for (int j = 0; j <= R; j++) {
      	 if(j) f[i][j] = min(f[i][j], f[l1][j - 1]);
      	 f[i][j] = min(f[i][j], f[l2][j] + 1);
	  }
   }
   for(int i = 0; i <= R; ++i) if(f[n][i] <= G) return true;
   return false;
}
int main(){
  n = read(), R = read(), G = read();
  if(R + G >= n) {puts("1"); return 0;}
  for (int i = 1; i <= n; i++) a[i] = read();
  sort(a + 1, a + n + 1);
  int l = 1, r = 1e9, Ans = r;
  while(l <= r) {
  	int mid = (l + r) >> 1;
  	if(Check(mid)) Ans = mid, r = mid - 1;
  	else l = mid + 1;
  }
  cout<<Ans;
  return 0;
}

算法二

\(f_{i, j}\) 表示用了 \(j\) 次红光,摧毁前 \(i\) 个位置的法坛,用的绿光的最少次数。

\(l1\) 是上一次使用红光的位置,\(l2\) 是上一次使用绿光的位置

转移:

\[f_{i, j} = \min\{f_{l_1, j - 1, }, f_{l_2, j} + 1\} \]

\(l_1\) 和 \(l_2\) 可以用双指针维护。

复杂度 \(O(n^2logn)\)

code

/*
work by: Ariel_
Knowledge:
Time:
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;
int read() {
  int x = 0, f = 1; char c = getchar();
  while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
  return x * f;
}
int n, R, G, a[MAXN], Ans, f[2010][2010];
bool Check(int L) {
   for (int i = 1; i <= n; i++) 
   	 for (int j = 0; j <= R; j++) f[i][j] = INF;
   int l1 = 0, l2 = 0;
   f[0][0] = 0;
   for (int i = 1; i <= n; i++){
      while(l1 < n && a[l1 + 1] + L <= a[i]) l1++;
      while(l2 < n && a[l2 + 1] + 2 * L <= a[i]) l2++;
      for (int j = 0; j <= R; j++) {
      	 if(j) f[i][j] = min(f[i][j], f[l1][j - 1]);
      	 f[i][j] = min(f[i][j], f[l2][j] + 1);
	  }
   }
   for(int i = 0; i <= R; ++i) if(f[n][i] <= G) return true;
   return false;
}
int main(){
  n = read(), R = read(), G = read();
  if(R + G >= n) {puts("1"); return 0;}
  for (int i = 1; i <= n; i++) a[i] = read();
  sort(a + 1, a + n + 1);
  int l = 1, r = 1e9, Ans = r;
  while(l <= r) {
  	int mid = (l + r) >> 1;
  	if(Check(mid)) Ans = mid, r = mid - 1;
  	else l = mid + 1;
  }
  cout<<Ans;
  return 0;
}

T3

次短路板子题

两种做法:

算法一

对每个点记录一个到达这个点的最短路和次短路。

考虑什么时候能更新最短路,什么时候能更新次短路。

特判考虑全(1:1, 2:1, 2:2)

code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int MAXN = 5010, MAXM = 200010;
int read() {
  int x = 0, f = 1; char c = getchar();
  while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
  while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
  return x * f;
}
int n, m, dis[MAXN][3], vis[MAXN];
struct edge{int v, nxt, w;}e[MAXM << 1];
int E, head[MAXN];
void add_edge(int u, int v, int w) {
  e[++E] = (edge){v, head[u], w};
  head[u] = E;
}
queue<int>q;
void Spfa(int s){
  memset(dis, 0x3f, sizeof dis);
  memset(vis, false, sizeof vis);
  q.push(1), dis[1][1] = 0;
  while(!q.empty()){
    int u = q.front();
    q.pop(), vis[u]=0;
    for(int i = head[u];i;i=e[i].nxt){
      int v = e[i].v;
      if(dis[v][1] > dis[u][1] + e[i].w){
        dis[v][2] = dis[v][1];
        dis[v][1] = dis[u][1] + e[i].w;
        if(!vis[v]) q.push(v), vis[v] = 1;
      }
      if(dis[v][2] > dis[u][2] + e[i].w){
        dis[v][2] = dis[u][2] + e[i].w;
        if(!vis[v]) q.push(v), vis[v] = 1;
      }
      if(dis[v][2] > dis[u][1] + e[i].w && dis[v][1] < dis[u][1] + e[i].w){
        dis[v][2] = dis[u][1] + e[i].w;
        if(!vis[v]) q.push(v), vis[v] = 1;
      }
    }
  }
}
int main() {
   n = read(), m = read();
   for (int i = 1, u, v, w; i <= m; i++) {
       u = read(), v = read(), w = read();
       add_edge(u, v, w), add_edge(v, u, w);
   }
   Spfa(1);
   printf("%d", dis[n][2]);
   system("pause");
   return 0;
}
上一篇:C# 特性


下一篇:C# app.config 配置文件的读取方法