Loading

NOIP 2017-day2

pts: 120

T1: 100

T2: 20

T3: 0

前言:瞅了一样 T3,区间删除和插入(据说是平衡树板子,可我不会平衡树啊/kk),去干 T2 ,为啥 T2 也炸了啊 /jk

总结:卷数据结构去 = =

T1

奶酪

搜索,并查集

solution

逃不过立体几何

不止岛为啥他们都在写搜索 = = 这不是并查集的大板子嘛

对于能连起来的洞用并查集并到一起,最后两两枚举能打通上边界的洞和下边界的洞是否能连起来就好了哇

两个球是否连接的条件:

两个球心的距离 \(\leq 2 * r\)

code

/*
work by:Ariel_  
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#define int long long
using namespace std;
const int N = 1000 + 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;
}
struct Pots{int x, y, z;}a[N];
int fa[N];
int find(int x) {return fa[x] == x ? fa[x] : find(fa[x]); }
int merage(int x, int y){fa[find(x)] = find(y);}
int calc(int p, int o) {
   return (a[o].x - a[p].x) * (a[o].x - a[p].x) + (a[o].y - a[p].y) * (a[o].y - a[p].y) + (a[o].z - a[p].z) * (a[o].z - a[p].z); 
}
int T, n, h, r, l_id[N], r_id[N], cnt_l, cnt_r, fag;
void clear(){
   memset(l_id, 0, sizeof l_id);
   memset(r_id, 0, sizeof r_id);
   cnt_l = 0, cnt_r = 0, fag = 0;
}
signed main(){
   T = read();
   while(T--) {
   	  n = read(), h = read(), r = read();
   	  for (int i = 1; i <= n; i++) a[i].x = read(), a[i].y = read(), a[i].z = read();     
      for (int i = 1; i <= n; i++) fa[i] = i;
      clear();
	  for (int i = 1; i <= n; i++) {
	     if (a[i].z + r >= h) r_id[++cnt_r] = i;
	     if (a[i].z - r <= 0) l_id[++cnt_l] = i;
	     for (int j = 1; j <= i; j++)
	       if(calc(i, j) <= 4 * r * r) merage(i, j);
	  }
	  for (int i = 1; i <= cnt_l; i++){
	  	for (int j = 1; j <= cnt_r; j++)
	  		  if (find(l_id[i]) == find(r_id[j])){fag = 1; break;}
		if(fag) break;
      }
      if(fag) printf("Yes\n");
      else printf("No\n");
   }
   return 0;
}

T2

[NOIP2017 提高组] 宝藏

贪心,枚举,暴力,状压 dp

solution

20 pts

树的情况,求出树的重心,对每个点跑最短路,然后更新答案就好了

70 pts

\(n = 8\) 真滴很小啊,直接枚举起点和开采顺序就好了 /kk

然后,直接枚举每个点由哪个点转移来的就好了

code

/*
work by:Ariel_
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#define int long long
using namespace std;
const int INF = 0x3f;
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, e[55][55], f[55], ans = 2147483645, tmp;
void dfs(int num){
   if (num == n) {
   	  ans = min(ans, tmp);
   	  return ;
   }
   if(tmp >= ans) return ;
   for (int i = 1; i <= n; i++) {
   	   if(f[i]) continue;
   	   for (int j = 1; j <= n; j++) {
   	   	   if(e[j][i] == 2147483645 || !f[j] || i == j) continue;
		   tmp += f[j] * e[j][i]; f[i] = f[j] + 1;
		   dfs(num + 1);
		   f[i] = 0; tmp -= f[j] * e[j][i]; 
		}
   }  
}
signed main(){
   n = read(), m = read();
    for(int i = 1; i <= n; i ++)
    for(int j = 1; j <= n; j ++)
      e[i][j] = 2147483645;																											
   for (int i = 1; i <= m; i++) {
   	  int u = read(), v = read();
   	  e[u][v] = e[v][u] = min(e[u][v], read());
   }
   for(int i = 1; i <= n; i++)
   	 f[i] = 1, dfs(1), f[i] = 0;
   printf("%lld", ans);
   puts("");
   return 0;
}

100 pts

搜索剪枝能过 ??

还是看看状压吧

题目可以看做找一个点作为根,生成一棵树,满足代价 $\sum dep[i] * dis[fa_i][i] $ 最小

其中 \(dep_i\) 表示 \(i\) 节点在这棵生成树中的深度(根节点深度为 \(0\), \(dis[fa_i][i]\) 表示 \(i\) 节点到他父亲节点的距离

因为 \(n \leq 12\) 所以考虑状压节点

状态:

\(f[i]\) 表示所选点的集合为 \(i\) 的最小花费,\(st_{i,j}\) 表示当前选的点的集合为 \(i\) 并且 \(i\) 状态取最优方案时,节点 \(j\) 的深度

转移:

\(f_{i∣2^{k}} = min({f_{i∣2^k},f_i + (dis[j][k] ∗ (st[i][j]+1))}\)

\(st_{i∣2^k} =st_{i,j}+1(j∈i~~\&~~k \notin i)\)

初始化:\(f_{2^S} = 0, st_{{2^s},s} = 0\)

时间复杂度

\(O(n^3 \times 2^n)\)

code

/*
work by:Ariel_
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 15;
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 dis[N][N], st[1 << N][N], f[1 << N], n, m, ans = 0x3f3f3f3f;
int work(int s){
   memset(f, 0x3f, sizeof f), memset(st, 0x3f, sizeof st);
   int lim = (1 << n);
   f[1 << s] = 0; st[1 << s][s] = 0;
   for (int i = 1; i < lim; i++){//枚举合法状态 
        if(f[i] >= 0x3f3f3f3f) continue;
		for (int j = 0; j < n; j++) { 
		  if(!(i & (1 << j))) continue;
		  for (int k = 0; k < n; k++) {
		  	 if(i & (1 << k)) continue;
		  	 if((dis[j][k] != 0x3f3f3f3f) && (f[i|(1 << k)]) > f[i] + (dis[j][k] * (st[i][j] + 1))){
		  	 	   f[i | (1 << k)] = f[i] + (dis[j][k] * (st[i][j] + 1));
		  	 	   memcpy(st[i | (1 << k)], st[i], sizeof(st[i | (1 << k)]));
                  st[i | (1 << k)][k] = st[i][j] + 1;
			   }
		  }
		}      
	} 
	return f[lim - 1];
}
int main(){
   n = read(), m = read();
   memset(dis, 0x3f, sizeof dis);
   for (int i = 1, u, v, w; i <= m; i++) {
   	  u = read(), v = read(), w = read();
   	  u--, v--;//方便二进制 
   	  dis[u][v] = min(dis[u][v], w); dis[v][u] = min(dis[v][u], w);
   }
   for (int i = 0; i < n; i++)//枚举根节点 
     ans = min(ans, work(i));
   printf("%d", ans);
   puts("");
   return 0;
}

T3

[NOIP2017 提高组] 列队

线段树,平衡树,树状数组

emm……

等学完平衡树再来写

posted @ 2021-06-09 10:05  Dita  阅读(45)  评论(0)    收藏  举报