2017/11/1模拟赛
磁星(magnet)
【题目描述】
在 B 城呆惯了的小 H 决定去外太空溜达一圈。
人类现已发现并开发的星球(包括小 H 所在的星球)有 n 个,
并且在这 n 个星球之中,人们发现了 m 对两个星球的关系。关系“x
y”表示的是星球 x 对星球 y 有 1 一个单位的引导力,由于引导力还
具有传递性,如果星球 x 对星球 y 能有恰好 a 个单位的引导力,星球
y 对星球 z 能有恰好 b 个单位的引导力,那么星球 x 对星球 z 就能有
恰好 a+b 个单位的引导力。 换言之,星球 x 对星球 y 的引导力可以是
x 到 y 的任意一条引导力路径的引导力总和。
人类利用现已发现的 n 个星球之间的引导力关系,在每个星球上
都建造了 p 个类型的传送枢纽,其中第 i 个类型的传送枢纽能够将人
从该星球传送到对该星球引导力为 2^(i-1)个单位的星球上。小 H 所
在的星球为 S,而他想去的星球为 T,他现在想知道他最少能传送几
次可以到达目的地呢?
【输入数据】
第一行五个正整数 n,m,p,S,T。
接下来 m 行,每行两个正整数“x y”表示一对引导力关系。
【输出数据】
输出只有一行,一个正整数,表示最少的传送次数。 如果不能到
达,请输出”-1”。
【样例输入】
4 4 3 1 4
1 1
2 1
3 2
4 3
【样例输出】
1
【数据范围】
对于 20%的数据, n<=5;
对于 40%的数据, n<=15;
另外 20%的数据, p<=10;
对于 100%的数据, 1<=n,m<=50, 1<=p<=31, 1<=S,T<=n。
【样例解释】
星球 1 对星球 1 的引导力为 1;
星球 2 对星球 1 的引导力为 2;
星球 3 对星球 1 的引导力为 3;
星球 4 对星球 1 的引导力为 4;
运用第 3 种传送枢纽可以直接从星球 1 传送到星球 4。
50分暴力骗分代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<vector> 4 #define INF 0x7fffffff 5 using namespace std; 6 int n,m,p,S,T,st[55],ct,a[55],b[55],ct2,pr[35],mx=INF; 7 bool zh[55],flag,flag2; 8 vector<int> G[55]; 9 void prework(){ 10 int tmp=1; pr[0]=1; 11 for(int i=1;i<=30;i++) tmp*=2,pr[i]=tmp; 12 } 13 void dfs(int u,int fa){ 14 st[++ct]=u; 15 if(u==T){ 16 flag=true; a[++ct2]=ct-1; 17 for(int i=1;i<=ct;i++) 18 if(zh[st[i]]){printf("1");exit(0);} 19 } 20 if(u==S&&fa!=0){flag2=true;return;} 21 for(int i=0;i<G[u].size();i++){ 22 if(G[u][i]==u) continue; 23 dfs(G[u][i],u); 24 }ct--; 25 } 26 int work(int x){ 27 int ret=0; 28 while(x){ 29 for(int i=p-1;i>=0;i--) 30 if(pr[i]<=x){x-=pr[i];ret++;break;} 31 }return ret; 32 } 33 int main() 34 { 35 freopen("magnet.in","r",stdin); 36 freopen("magnet.out","w",stdout); 37 prework(); 38 scanf("%d%d%d%d%d",&n,&m,&p,&S,&T); 39 for(int i=1;i<=m;i++){ 40 int x,y; scanf("%d%d",&x,&y); 41 G[y].push_back(x); 42 if(x==y) zh[i]=true; 43 }dfs(S,0); 44 if(!flag){printf("-1");return 0;} 45 if(!flag2){ 46 for(int i=1;i<=ct2;i++) b[i]=work(a[i]); 47 for(int i=1;i<=ct2;i++)if(b[i]<mx) mx=b[i]; 48 printf("%d",mx); 49 return 0; 50 } 51 printf("1"); 52 return 0; 53 }
变量声明:f[i][j][k]表示j到k有长度为2^(i-1)的路径。
题解:首先,我们可以用类似于倍增的方法预处理,将所有能互相到达的的点连边,时间复杂度O(pn^3),然后bfs即可。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 int n,m,p,S,T,q[55],g[55]; 5 bool f[35][55][55]; 6 int bfs(){ 7 int hd=1,tl=1; g[q[hd]=S]=1; 8 while(hd<=tl){ 9 if(q[hd]==T) break; 10 int u=q[hd++]; 11 for(int i=1;i<=n;i++) 12 if(!g[i])for(int j=1;j<=p;j++) 13 if(f[j][u][i]){g[i]=g[u]+1; q[++tl]=i; break;} 14 } 15 return hd<=tl?g[T]-1:-1; 16 } 17 int main() 18 { 19 freopen("magnet.in","r",stdin); 20 freopen("magnet.out","w",stdout); 21 scanf("%d%d%d%d%d",&n,&m,&p,&S,&T); 22 for(int i=1;i<=m;i++){ 23 int x,y; scanf("%d%d",&x,&y); 24 f[1][y][x]=true; 25 } 26 for(int i=2;i<=p;i++) 27 for(int x=1;x<=n;x++) 28 for(int y=1;y<=n;y++) 29 for(int z=1;z<=n;z++) 30 f[i][x][y]|=f[i-1][x][z]&&f[i-1][z][y]; 31 printf("%d",bfs()); 32 return 0; 33 }
技能(technique)
【题目描述】
众所周知,小 C 有一块 n*n 的棋盘。
本来棋盘上有黑白两种颜色,其中从左上(1,1)到右下(n,n)的主对
角线上都是黑格。然而几天前小 W 叫来了几车面包人企图掀翻小 C
的棋盘,虽然小 C 机智地化解了面包人的进攻,但小 C 还是发现,
棋盘的格子被一股来自东方的神秘力量,打乱了。
于是小C开始修复他的棋盘,他的方法是:每次选择棋盘中不同
的两行(或两列),交换这两行(列)的颜色。小 C 并不苛求把棋盘
100%复原回原来的样子,只希望能够恢复原本的特征——“从左上
(1,1)到右下(n,n)的主对角线上都是黑格”即可。他希望知道这个方法
是否可行,毕竟有规律地还原事物也是一项重要的技能啊。
【输入数据】
第一行一个正整数 T, 表示数据组数。
接下来 T 组数据。每组数据第一行一个正整数 n,表示棋盘边长,
接下来 n 行,每行一个长度为 n 的 01 字符串,描述一个打乱后的 n*n
的棋盘。
【输出数据】
输出共 T 行,对于每组数据输出一行,如果能用题中所述方法把
棋盘特征复原,输出”Yes”,否则输出”No”。
【样例输入】
1 3
0 1 0
1 0 0
0 0 1
【样例输出】
Yes
【数据范围】
对于 20%的数据, n<=10;
对于 40%的数据, n<=50;
对于 60%的数据, n<=100;
另外 10%的数据,保证有某一行或某一列全为 0。
对于 100%的数据, 1<=n<=500, 1<=T<=4。
【样例解释】
交换 1,2 两行。 矩阵变为{{1,0,0},{0,1,0},{0,0,1}}。
题解:由于操作可逆,容易发现,题目可以转换为每行选一个1,使得每列都有一个1。这题就转换为二分图匹配,s向所有行连1,所有列向t连1,若a[i][j]=1,i行向j列连1,网络流即可。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define MN 10005 5 #define INF 0x7fffffff 6 using namespace std; 7 struct edge{int to,cap,next,rev;}e[MN*6]; 8 int s,t,n,m,cnt,head[MN],lev[MN],q[MN]; 9 void prework(){ 10 cnt=0; memset(head,0,sizeof(head)); 11 memset(q,0,sizeof(q)); 12 memset(e,0,sizeof(e)); 13 } 14 void ins(int u,int v,int w){ 15 e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].cap=w;e[cnt].rev=cnt+1; 16 e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;e[cnt].cap=0;e[cnt].rev=cnt-1; 17 } 18 bool bfs(){ 19 memset(lev,-1,sizeof(lev)); 20 int hd=0,tl=1; 21 lev[s]=0; q[hd]=s; 22 while(hd<tl){ 23 int v=q[hd++]; 24 for(int i=head[v];i;i=e[i].next) 25 if(e[i].cap>0&&lev[e[i].to]<0){ 26 lev[e[i].to]=lev[v]+1; 27 q[tl++]=e[i].to; 28 } 29 } 30 if(lev[t]==-1) return false; 31 return true; 32 } 33 int dfs(int u,int f){ 34 if(u==t) return f; 35 int used=0; 36 for(int i=head[u];i;i=e[i].next) { 37 if(e[i].cap>0&&lev[u]<lev[e[i].to]){ 38 int w=dfs(e[i].to,min(f-used,e[i].cap)); 39 if (w>0){ 40 e[i].cap-=w; e[e[i].rev].cap+=w; used+=w; 41 if(used==f) break; 42 } 43 } 44 } 45 if(!used) lev[u]=-1; 46 return used; 47 } 48 int dinic(){ 49 int flow=0; 50 while(bfs()) flow+=dfs(s,INF); 51 return flow; 52 } 53 void init(){ 54 scanf("%d",&n); s=0; t=2*n+1; 55 for(int i=1;i<=n;i++) 56 for(int j=1;j<=n;j++){ 57 char c; cin>>c; 58 if(c=='1') ins(i,j+n,1); 59 } 60 for(int i=1;i<=n;i++) ins(s,i,1),ins(i+n,t,1); 61 } 62 int main() 63 { 64 freopen("technique.in","r",stdin); 65 freopen("technique.out","w",stdout); 66 int T; scanf("%d",&T); 67 while(T--){ 68 prework(); init(); 69 if(dinic()==n) printf("Yes\n"); 70 else printf("No\n"); 71 } 72 return 0; 73 }
社会主义西红柿(tomato)
【题目描述】
Kirby 是一个超级吃货, 其中 Maxim tomato 是 Kirby 最喜爱的食
物。 其他食物 Kirby 都可以强忍住不吃,唯有 Maxim tomato 对于他
来说,是不可抗拒的存在,这也就是 Kirby 现在被 Yin Yarn 变成毛线
扔在 Patch Land 上的原因。
所幸从 Yin Yarn 那里偷吃来的 Maxim tomato 给了 Kirby 很大的
增益,使得 Kirby 能够变化成很多东西进行更加快捷的行动。 Kirby
现在的任务是收集散落在 Patch Land 各处的宝石。 Patch Land 可以类
似地看做一根坐标轴, 一开始 Kirby 在原点,也是 Patch Land 的最左
端, Patch Land 的最右端位于 n。 Patch Land 上有 m 个地方散落着宝
石, 位置分别为 pi,该宝石的价值为 wi。 Kirby 在每个位置都有 k 种
行动方式,其中第 i 种行动方式是从当前位置向右跳到距离当前位置
为 di 的位置, 当然 Kirby 也可以选择就此收手,不做任何行动。 Kirby
每到一个位置都会捡起所在位置的所有宝石。所以 Kirby 想请你规划
一下路线,使他能获得的宝石价值总和最大。
“实在搞不懂为什么 Yin Yarn 的头顶会长着一颗 Maxim tomato,
真是越来越难猜测作者的想法了„„” Kirby 无奈地喃喃自语。
【输入数据】
第一行一个正整数 n,m,k,表示 Patch Land 的长度、宝石个数和
行动种数;
第二行 k 个正整数,表示每种行动的移动距离 di;
接下来 m 行,每行两个整数, pi 和 wi,表示宝石的位置和价值。
【输出数据】
输出只有一行,最大的能获得的宝石价值总和。
【样例输入】
20 4 2
4 7
4 2
7 3
11 2
14 1
【样例输出】
5
【数据范围】
对于 10%的数据, n,m<=10, k<=3;
对于 25%的数据, n,m<=1000;
对于 40%的数据, n<=10^6;
另外 10%的数据, k=1;
另外 20%的数据, wi>0;
对于 100%的数据, 1<=n<=10^9, 1<=m<=10^5, 0<=pi<=n,
0<=|wi|<=10^9, 1<=k,di<=8, 保证 di 互不相同。
【样例解释】
先跳 7 格,再跳 4 格,依次经过 0,7,11。总和为 0+3+2=5。
题解:还没写