Loading

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 = 2010;
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], f[MAXN][MAXN], Ans, p[MAXN], q[MAXN];
bool Check(int L){
  memset(f, 0, sizeof f), memset(p, 0, sizeof p), memset(q, 0, sizeof q);
  p[n + 1] = n, q[n + 1] = n;
  int cur_1 = 1, cur_2 = 1;
  for (int i = 1; i <= n; i++) {
  	while(cur_1 < n && a[i] + L - 1 >= a[cur_1 + 1]) cur_1++;
  	p[i] = cur_1;
  	while(cur_2 < n && a[i] + 2 * L - 1 >= a[cur_2 + 1]) cur_2++;
    q[i] = cur_2;
  }
  for (int i = 0; i <= R; i++) {
    for (int j = 0; j <= G; j++){
      if(i) f[i][j] = max(p[f[i - 1][j] + 1], f[i][j]);
	  if(j) f[i][j] = max(q[f[i][j - 1] + 1], f[i][j]);
	}
  }
  return f[R][G] >= n;
}
int main(){
  n = read(), R = read(), G = read();
  for (int i = 1; i <= n; i++) a[i] = read();
  if(R + G >= n) {printf("1"); return 0;} 
  sort(a + 1, a + n + 1);
  int l = 1, r = 1e9, Ans = 0x3f3f3f3f;
  while(l <= r) {
  	int mid = (l + r) >> 1;
  	if(Check(mid)) Ans = mid, r = mid - 1;
  	else l = mid + 1;
  }
  printf("%d", 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;
}
posted @ 2021-10-20 07:41  Dita  阅读(29)  评论(2编辑  收藏  举报