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<=1000k<=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<=100000wi<=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,不会。

 

posted @ 2017-10-16 16:28  Beginner_llg  阅读(204)  评论(0编辑  收藏  举报