2017/10/13模拟赛
密室逃脱(room)
【问题描述】
你在玩密室逃脱, 所有房间组成了一个 n 行 m 列的矩阵, 一些
房间上了锁。 一开始你在某个房间里, 你的目标是逃到边界上(第 1
行或第 n 行或第 1 列或第 m 列) 的任意一个房间中。 你可以进行若
干轮操作, 每轮操作你可以先移动至多 k 次, 每次可以移动到四相邻
(上下左右) 的一个未上锁的房间中, 完成移动后, 你可以再选择至
多 k 个房间并解锁这些房间, 然后完成这一轮操作。 你想知道你至少
要进行几轮操作才可以达成目标。
【输入格式】
第一行三个正整数 n,m,k, 意义同问题描述。
接下来 n 行, 每行 m 个字符, 描述这个矩阵。 若字符为’.’, 表
示在这个位置上的是一个没上锁的房间; 若字符为’#’, 表示在这个位
置上的是一个上了锁的房间; 若字符为’S’, 表示在这个位置上的是
一个没上锁的房间并且是你的初始位置, 保证这样的字符只有一个并
且不在边界上。
【输出格式】
输出一个整数, 表示答案。
【样例输入】
5 5 1
#####
#...#
##S##
#...#
#####
【样例输出】
2
【数据范围】
对于 40%的数据, n,m<=100;
对于 100%的数据, n,m<=1000, k<=n*m。
题解:假设我们跑完第一次后,之后就不用考虑锁住的房间啦,所以我们广搜处理出第一次能遍历到的所有点离四周的距离,然后最小值/k+1即可。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #define MN 1005 4 using namespace std; 5 const int dx[4]={-1,0,0,1},dy[4]={0,-1,1,0}; 6 char s[1005][1005]; 7 int n,m,k,sx,sy,ans[4],dis[MN][MN],ansx=MN; 8 bool vis[MN][MN]; 9 struct node{int x,y;}que[MN*MN]; 10 void bfs(){ 11 for(int i=0;i<4;i++) ans[i]=MN; 12 que[0].x=sx;que[0].y=sy;vis[sx][sy]=1; 13 int hd=0,tl=0; 14 while(hd<=tl){ 15 node tmp=que[hd++]; 16 for(int i=0;i<4;i++){ 17 int tx=tmp.x+dx[i],ty=tmp.y+dy[i]; 18 if(vis[tx][ty]||s[tx][ty]=='#'||tx<1||tx>n||ty<1||ty>m) continue; 19 vis[tx][ty]=1; dis[tx][ty]=dis[tmp.x][tmp.y]+1; 20 if(dis[tx][ty]<k) que[++tl].x=tx,que[tl].y=ty; 21 if(tx-1<ans[0]) ans[0]=tx-1; 22 if(n-tx<ans[1]) ans[1]=n-tx; 23 if(ty-1<ans[2]) ans[2]=ty-1; 24 if(n-ty<ans[3]) ans[3]=n-ty; 25 } 26 } 27 } 28 int main() 29 { 30 freopen("room.in","r",stdin); 31 freopen("room.out","w",stdout); 32 scanf("%d%d%d",&n,&m,&k); 33 for(int i=1;i<=n;i++){ 34 scanf("%s",s[i]+1); 35 for(int j=1;j<=m;j++)if(s[i][j]=='S')sx=i,sy=j; 36 }bfs(); 37 for(int i=0;i<4;i++) ansx=min(ansx,ans[i]); 38 printf("%d",1+(ansx%k==0?ansx/k:ansx/k+1)); 39 return 0; 40 }
最大割(cut)
【问题描述】
有一张带权连通无向图,你看它不爽想要删掉若干条边把它分成
恰好两个连通块, 并且希望删掉的边权和最大, 于是你需要计算出这
个最大值。
【输入格式】
第一行两个正整数 n 和 m, 分别表示点数和边数。
接下来 m 行, 每行三个正整数 xi,yi,wi, 其中 xi,yi 表示一条边的
两个端点, wi 表示这条边的边权。
【输出格式】
输出一个整数, 表示答案。
【样例输入】
3 3
1 2 1
2 3 2
3 1 3
【样例输出】
5
【数据范围】
对于 30%的数据, n,m<=20;
对于 50%的数据, n,m<=1000;
对于 100%的数据, 2<=n,m<=100000, wi<=10000;
题解:最小生成树减去一条最大的边即为答案。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define MN 100010 5 using namespace std; 6 struct edge{int x,y,val;}e[MN]; 7 int n,m,fa[MN],ans,mx,sum; 8 bool cmp(edge a,edge b){return a.val<b.val;} 9 int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} 10 void unite(int x,int y){ 11 int fx=find(x),fy=find(y); 12 if(fx==fy) return; 13 fa[fx]=fy; 14 } 15 void kruskal(){ 16 sort(e+1,e+1+m,cmp); 17 for(int i=1;i<=n;i++) fa[i]=i; 18 for(int i=1;i<=m;i++){ 19 edge zhouzhenged=e[i]; 20 if(find(zhouzhenged.x)!=find(zhouzhenged.y)){ 21 unite(zhouzhenged.x,zhouzhenged.y); 22 ans-=zhouzhenged.val; mx=max(mx,zhouzhenged.val); 23 } 24 } 25 } 26 int main() 27 { 28 freopen("cut.in","r",stdin); 29 freopen("cut.out","w",stdout); 30 scanf("%d%d",&n,&m); 31 for(int i=1;i<=m;i++){ 32 scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].val); 33 ans+=e[i].val; 34 } 35 kruskal(); 36 printf("%d",ans+mx); 37 return 0; 38 }
粉刷匠(painter)
【问题描述】
作为粉刷界的毒瘤, 你坚信刷墙不需要视力, 于是你总是蒙着眼
睛刷墙。 正好一位客人需要把墙刷的具有艺术气息, 不要那么规律,
于是请到了你。 这位客人的墙可以描述成一个 n 行 m 列的矩阵, 一
些格子已经刷上了颜色, 客人需要你把每行每列都至少刷上一个格
子。 由于你蒙着眼睛, 所以你每次都会在墙上等概率随机一个格子刷
上颜色, 无论这个格子是否已经刷过。 你想知道你要完成任务的期望
刷墙次数。
【输入格式】
第一行两个正整数 n 和 m, 意义同问题描述。
接下来 n 行, 每行 m 个 0 或 1 的整数, 若数字为 0 表示这个格
子还没刷过, 若数字为 1 表示这个格子已经刷过。
【输出格式】
输出一个实数, 表示答案。 如果你的答案与标准答案相差不超过
10-4 , 判为正确。
【样例输入】
2 2
1 1
0 0
【样例输出】
2
【数据范围】
对于 30%的数据, n,m<=4;
对于 50%的数据, n,m<=10;
对于 70%的数据, n,m<=100;
对于 100%的数据, n,m<=1000;
题解:期望dp,不会。