题目按难度从简到难手动排序————题记
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
h−d的山峰的前提下,无法到达高度比
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
1101
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;
}