2019 HL暑假集训 Day8

题目按难度从简到难手动排序————题记

T1.朋友圈(friends.pas/c/cpp)

【背景】

朋友圈有时会传播谣言。

【问题描述】

现在有 n n n 个人、 m m m 个朋友圈,这 n n n 个人的 i d id id 分别是 1-n。大部分人都加入了一个或多个朋友圈,而有的人可能没进朋友圈,有的朋友圈里可能没有人。如果有一个人听到了谣言,他会把这个谣言发布到他加入的所有朋友圈中,所有看到这条谣言的人又会把这条谣言发布到他们所在的朋友圈中,这样谣言就会散布开来。
现在你得到了 $m 个朋友圈中的人员名单,问你对于 n n n 个人中的每一个人,如果这个人散播谣言,那么最多会有多少个人听到谣言。

【输入】

第一行两个整数 n , m n,m n,m
接下来 m m m
每行开头是一个整数 r r r,表示这个朋友圈里有多少个人
之后有 r r r 个整数,表示这个朋友圈里的人的 i d id id
同一行的所有整数用空格隔开

【输出】

一行 n n n 个用空格隔开的整数,表示对于 i d id id 1 , 2 , … , n 1,2,…,n 1,2,,n 的人散播谣言会让最多多少人听到

【输入输出样例 1】

I n p u t Input Input
7 5
3 2 5 4
0
2 1 2
1 1
2 6 7
O u t p u t Output Output
4 4 1 4 4 2 2

【数据范围】

对于 30%的数据: n , m < = 10 n,m<=10 n,m<=10
对于 60%的数据: n , m < = 1000 n,m<=1000 n,m<=1000
对于 100%的数据:n,m<= 1 0 5 10^5 105 c i c_i ci<= 1 0 5 10^5 105,朋友圈中不会有两个相同的人.


心路历程:拿到题,读完T1,心里有点小激动,一眼看出正解并查集, 20 m i n 20min 20min码完,一遍跑过样例,很资瓷啊,检查了一下,看T2去了。
正解:并查集基本操作。


正解代码:
#include <bits/stdc++.h>
using namespace std;

const int N = 1000001;
int n, m;
int c[N], a[N], tot = 0;
int Big[N] = {};
int fa[N];

inline int getf(int x) {
	if (x == fa[x]) return x;
	return fa[x] = getf(fa[x]);
}

int main() {
	freopen("friends.in","r",stdin);
	freopen("friends.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) fa[i] = i, Big[i] = 1;
	for (int i=1;i<=m;i++) {
		int k;
		scanf("%d",&k);
		for (int j=1;j<=k;j++) {
			scanf("%d",&a[j]);
		}
		for (int j=2;j<=k;j++) {
			int x = getf(a[j]), y = getf(a[j-1]);
			if (x != y) {
				fa[x] = y;
				Big[y] += Big[x];
			}
		}
	}
	for (int i=1;i<=n;i++) printf("%d ",Big[getf(i)]);
	return 0;
}

T2.登 峰 造 极 (dfzj.pas/c/cpp)

【背景】

几十年前,一位长者路过鲁迅故里。
看着高兀奇峰,他不禁发出感慨:
登 峰 造 极 !

【问题描述】

从天上看,山峦整齐地排列成 n n n m m m列,第 i i i行第 j j j列的山峰高度为 h [ i ] [ j ] h[i][j] h[i][j]
如果从一个高度为h的山峰开始,在不经过高度小于等于 h − d h-d hd的山峰的前提下,无法到达高度比 h h h高的峰,那么我们称这座山峰登峰造极。
你需要告诉他,有几座山峰登峰造极。

【输入】

第一行两个空格隔开的整数 n n n m m m
接下来 n n n行,每行 m m m个空格隔开的整数,表示山峰的高度

【输出】

一行一个整数,含义如题

【输入输出样例1】

I n p u t Input Input:
6 10 2
0 0 0 0 0 0 0 0 0 0
0 1 2 1 1 1 1 0 1 0
0 2 1 2 1 3 1 0 0 0
0 1 2 1 3 3 1 1 0 0
0 2 1 2 1 1 1 0 2 0
0 0 0 0 0 0 0 0 0 0

O u t p u t Output Output:
4

【数据范围】

对于30%的数据: n , m < = 10 n,m<=10 n,m<=10
对于100%的数据: n , m < = 500 n,m<=500 n,m<=500


心路历程:题目十分简洁,题意十分显然,可是我就是看不懂怎么办,没办法,在读懂题意无解的情况下,我看向T3,心中有些小激动。
正解
正解就是从高到低开始bfs
分类:如果途径所有点都没来过,那么途经的同样高的山成功登峰造极
如果发现能走到之前更高的山走过的地方,那就打个标记,在整个 b f s bfs bfs完成后再扫一遍队列,把所有经过的点都安排上。
–—— cdc的solution
上面的方法既抽象,代码量又大,看我的暴力解标算。
对于任意一个点,我们进行 d f s dfs dfs,向四个方向判断,如果满足题意,就继续向下搜;如果不符合题意,就立即退出。最后累加登峰造极的个数。
——— zcc 的暴力solution


正解代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int rd(){int z=0,mk=1;  char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')mk=-1;  ch=getchar();}
	while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
	return z*mk;
}
int fx[4]={1,-1,0,0},fy[4]={0,0,1,-1};
struct nds{int x,y,z;}b[251000];
int n,m,o,a[510][510];
int q[251000],hd=0;
int vstd[510][510],vtp=0;
int ans=0;
int gtid(int x,int y){  return (x-1)*m+y-1;}
bool cmp(nds x,nds y){  return x.z<y.z;}
bool chck(int x,int y){  return x>=1&&x<=n&&y>=1&&y<=m;}
void bfs(int _x,int _y){
	if(vstd[_x][_y])  return ;
	++vtp;
	q[hd=1]=gtid(_x,_y);  vstd[_x][_y]=vtp;
	bool flg=false;
	for(int k=1;k<=hd;++k){
		int x=q[k]/m+1,y=q[k]%m+1;
		for(int i=0;i<4;++i)
			if(chck(x+fx[i],y+fy[i]) && a[x+fx[i]][y+fy[i]]>a[_x][_y]-o){
				if(vstd[x+fx[i]][y+fy[i]]&&vstd[x+fx[i]][y+fy[i]]!=vtp)  flg=true;
				else if(!vstd[x+fx[i]][y+fy[i]]){
					vstd[x+fx[i]][y+fy[i]]=vtp;
					q[++hd]=gtid(x+fx[i],y+fy[i]);
				}
			}
	}
	if(!flg)
		for(int i=1;i<=hd;++i)
			ans+=(a[q[i]/m+1][q[i]%m+1]==a[_x][_y]);
	return ;
}
int main(){
	//freopen("ddd.in","r",stdin);
	freopen("dfzj.in","r",stdin);
	freopen("dfzj.out","w",stdout);
	memset(vstd,0,sizeof(vstd));
	cin>>n>>m>>o;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			a[i][j]=rd();
			b[gtid(i,j)+1]=(nds){i,j,a[i][j]};
		}
	sort(b+1,b+n*m+1,cmp);
	for(int i=n*m;i>=1;--i)
		bfs(b[i].x,b[i].y);
	cout<<ans<<endl;
	return 0;
}
//代码冗长难懂。。。。
zcc的暴力代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1000;
int n, m, d;
int a[N][N];
int flag = 0;
int vis[N][N];
int num = 0, dat = 0;
int ans = 0;

void dfs(int x,int y) {
	vis[x][y] = dat;
	if (a[x][y] > num) {
		flag = 1;
		return;
	}
	if (a[x+1][y]>num-d && x!=n && vis[x+1][y] != dat) dfs(x+1, y);
	if (flag) return;
	if (a[x-1][y]>num-d && x!=1 && vis[x-1][y] != dat) dfs(x-1, y);
	if (flag) return;
	if (a[x][y+1]>num-d && y!=m && vis[x][y+1] != dat) dfs(x, y+1);
	if (flag) return;
	if (a[x][y-1]>num-d && y!=1 && vis[x][y-1] != dat) dfs(x, y-1);
}

int main() {
	freopen("summits.in","r",stdin);
	freopen("summits.out","w",stdout);
	scanf("%d%d%d",&n,&m,&d);
	int Max = -1, sum = 0;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++) scanf("%d",&a[i][j]), Max = max(a[i][j],Max);
	for (int i=1;i<=n;i++) {
		for (int j=1;j<=m;j++) {
			flag = 0;
			num = a[i][j], dat = (i-1) * m + j;
			dfs(i, j);
			if (!flag) ans ++;
		}
	}
	printf("%d",ans);
	return 0;
}
//代码又短又好懂, ε≡٩(๑>₃<)۶

T3 骑士精神 (spirit.pas/c/cpp)

【背景】

这是一道经典题。

【问题描述】

在一个 5 × 5 5×5 5×5的棋盘上有 12 12 12个白色的骑士和 12 12 12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为 1 1 1,纵坐标相差为 2 2 2或者横坐标相差为 2 2 2,纵坐标相差为 1 1 1的格子)移动到空位上。 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘:

为了体现出骑士精神,他们必须以最少的步数完成任务。

【输入】

第一行有一个正整数 T ( T < = 10 ) T(T<=10) T(T<=10),表示一共有N组数据。接下来有 T T T 5 × 5 5×5 5×5的矩阵, 0 0 0表示白色骑士, 1 1 1表示黑色骑士, ∗ * 表示空位。两组数据之间没有空行。

【输出】

对于每组数据都输出一行。如果能在 15 15 15步以内(包括 15 15 15步)到达目标状态,则输出步数,否则输出 − 1 -1 1

【输入输出样例1】

I n p u t Input Input:
2
10110
0111
10111
01001
00000
01011
110
1
01110
01010
00100
O u t p u t Output Output:
7
-1

【数据范围】

T < = 10 T<=10 T<=10


心路历程:衔接上文(我心里有些小激动):原因很简单,因为骑士精神知道 I D A ∗ IDA* IDA经典题,我在以前无聊翻洛谷的时候翻到过,而且知道这道题需要迭代加深,拥有比其他选手更好的解题条件(但和那帮骑士精神做过的 d a l a o dalao dalao没法比),开始打代码…开始查错(一个超隐蔽的 B u g Bug Bug,查了我 1 h 1h 1h+)…开始剪枝…秒过样例。但是,我评测结果只有20分,why?很简单,先劈自己几个巴掌再说。当时写程序的时候,为了加快出答案的速度,我将迭代加深的层数只开到了7层,过了样例后我很兴奋,结果就忘记改回去了,我*,然后就只有20分了。悔得连肠子都青了。

正解 :迭代加深 +IDA*


正解代码:
#include <bits/stdc++.h>
using namespace std;

const int N = 20;
char a[N][N];
int Foot = 0;
int x1, Y1;
int n, m;
char b[N][N] = {
{'1','1','1','1','1'},
{'0','1','1','1','1'},
{'0','0','*','1','1'},
{'0','0','0','0','1'},
{'0','0','0','0','0'}};
int dx[N] = {-1,-2,-2,-1,1,2,2,1};
int dy[N] = {-2,-1,1,2,2,1,-1,-2};

inline int check(int &l,int &r) {
	int sum = 0;
	for (int i=0;i<5;i++){
		for (int j=0;j<5;j++) {
			if (a[i][j] == '*') l = i, r = j;
			if (a[i][j] != b[i][j]) sum++;
		}
	}
	return sum;
}

void Input() {
	for (int i=0;i<5;i++)
		for (int j=0;j<5;j++) {
			cin>>a[i][j];
		}
}

inline int dfs(int t) {
	int l,r;
	int kk = check(l,r);
	if (t+kk>Foot+1) return 0;
	if (!kk) return 1;
	for (int i=0;i<8;i++) {
		int x = l + dx[i], y = r + dy[i];
		if (x<0 || x>4 || y<0 || y>4) continue;
		swap(a[x][y], a[l][r]);
		if (dfs(t+1)) return 1;
		swap(a[x][y], a[l][r]);
	}
	return 0;
} 

int main() {
	freopen("spirit.in","r",stdin);
	freopen("spirit.out","w",stdout);
	int T;
	scanf("%d",&T);
	while (T--) {
		Input();
		bool flag = 1;
		for (Foot=1;Foot<=15;Foot++) {
			if (dfs(0)) {
				flag = 0;
				break;
			}
		}	
		if (!flag) printf("%d\n",Foot);
			else printf("-1\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值