除了A+B,应该是第一次写5星题吧QAQ,其实题并不是多难,主要是细节细节哇QAQ.........
描述 Description
“神州十号”飞船已经圆满发射成功了,你可知道飞船的顺利运行离不开地面对其的测控吗?因而必须保证飞船运行的轨道被测控站的雷达所全部覆盖,才能正常接受和传输信号。而由于测控站所控制的范围越小,信号接收情况越好,技术难度也越低。所以我们希望在覆盖整个轨道的前提下,使得测控站所需控制的范围越小越好。由于条件限制,最多只能建立K个测控范围相同的测控站,而且并不是任何位置都可以建立测控站的,所以需要找出一种方案,使得所需测控的范围尽量小。虽然离真正的模型还有一定差距,但喜欢编程的你当然对此问题跃跃欲试了。
为了你的方便,已经帮你把地球抽象展开成一个N*2M的平面矩阵(N为奇数)。其中前M列是东半球,后M列是西半球,最中间的一行为赤道。为了简化问题,飞船的轨道可以看作是在一条穿越东西半球的经线上空(虽然实际并非如此),在此矩阵表示为第I列和第I+M列(1<=I<=M)两端对接形成的一个环。矩阵的每一个格子中可以建立一个监测站,但某些格子除外(比如其他国家的领土或是条件恶劣的地区)。假设测控半径为R,则每个测控站的控制范围可以向上向下各延伸R个格子,即总长度为2R+1的区间。测控范围的重叠并不会相互影响,并且显然有R>=0且2R+1<=N(因为地球是圆的,测控范围最多只能是一个半球)。
由于飞船有M条可选轨道,所以需要你计算出对于每条轨道,最小的测控半径是多少。注意由于控制的需要,对于任意一条轨道,在东半球的赤道上都必须建立一个测控站,并且保证在东半球的赤道上建站不会有限制。
输入格式 Input Format
输入的第一行为三个正整数N,M,K表示矩阵的大小以及测控站的数量。保证有(2<=N,M<=1000,2<=K<=2N,N为奇数)。接下来N行,每行2M个字符,如果是1则表示可以放置测控站,否则为0表示无法放置。(保证中间一行的前M个字符一定为1)
输出格式 Output Format
输出包含M行,每行包含一个正整数,第I行表示第I条轨道所需最小的测控半径R为多少(保证必然有解),轨道从左到右依次编号。
样例输入 Sample Input
5 2 4
0110
0000
1100
0011
1000
样例输出 Sample Output
1
2
注释 Hint
对于轨道1:从第一行第一列开始向下可以展开为0010101001(首尾相连)
对于轨道2:从第一行第二列开始向下可以展开为1010001000(首尾相连)
两条轨道均把可选点全部建站即可。
数据规模:
对于30%的数据,有N,M<=15
对于60%的数据,有N,M<=100
对于100%的数据,有N,M<=1000
题值得一写,会让你更加注重细节
思路:二分+贪心,二分肯定都会,直接二分半径。而且他还给你了东半球赤道一定会建飞船控制站,所以每次检查就是从这个赤道开始。
但是怎么检查呢???
既然给你了做多只能建k个飞船控制站,所以你就得是在这个半径R下建最少的飞船控制站来覆盖整个轨迹。然后就从赤道开始覆盖,在覆盖的同时
直接记录下一个理这个控制站最远并且能覆盖到的控制站(如果你先覆盖在去查找会超时的,别问我怎么知道的)
具体详解见程序里的标注:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int a[2100][2100]; 8 int b[2100]; 9 int n,m,k; 10 bool f[2100]; 11 int la=0; 12 int cover(int q,int x,int now)//覆盖 13 { 14 int id=-1; 15 if(q==1)//向前覆盖 16 { 17 int sum=0; 18 for(int i=now;;i--) 19 { 20 if(i<1) 21 i+=n*2; 22 if(!f[i]) 23 la++; 24 f[i]=true; 25 if(sum==x) 26 break; 27 sum++; 28 } 29 } 30 else//向后覆盖 31 { 32 int sum=0,wr=0; 33 for(int i=now;;i++) 34 { 35 if(sum<=x)//先覆盖 36 { 37 38 if(i>n*2) 39 i-=n*2; 40 if(b[i]) 41 id=i; 42 if(!f[i]) 43 la++;//记录覆盖的次数 44 f[i]=true; 45 sum++; 46 } 47 else if(wr<=x)//注:因为你要查找下一个飞船控制站可以和这个飞船控制站把这两个控制站中间的所有路线覆盖住,所以再向下找一个半径x 48 { 49 if(x==0)//if语句的顺序一定要注意,我就这样错了 50 break; 51 if(i>n*2) 52 i-=n*2; 53 if(b[i]) 54 id=i; 55 wr++; 56 } 57 else 58 break; 59 } 60 } 61 return id; 62 } 63 bool check(int x) 64 { 65 la=0; 66 int K=0; 67 memset(f,0,sizeof(f)); 68 int now=n/2+1; 69 for(int i=now;;) 70 { 71 bool wa=false; 72 if(i>n*2) 73 i-=n*2; 74 if(K==k) 75 break; 76 if(b[i]==1) 77 { 78 int j=i; 79 K++; 80 cover(1,x,i);//覆盖 81 i=cover(0,x,i);//记录下一个飞船控制站并且直接跳到下一个点 82 if(j!=i)//如果i被更新了,就是说找到下一个飞船控制站了 83 wa=true; 84 } 85 if(la==2*n)//如果此时所有的路程全被覆盖了直接退出 86 return true; 87 if(wa&&i==now)//如果i被更新了,并且又从新回到了赤道 88 break; 89 int ha=0; 90 if(!wa)//如果i没背更新 91 i++; 92 93 } 94 return (la==n*2); 95 } 96 int search()//二分查找 97 { 98 int l=0,r=n*2+1,mid; 99 while(l+1<r) 100 { 101 mid=(l+r)>>1; 102 if(check(mid)) 103 r=mid; 104 else 105 l=mid; 106 } 107 if(check(l)) 108 return l; 109 else 110 return r; 111 } 112 int main() 113 { 114 //freopen("add.out","w",stdout); 115 memset(b,-1,sizeof(b)); 116 scanf("%d%d%d",&n,&m,&k); 117 char s[2100]; 118 for(int i=1;i<=n;i++) 119 { 120 scanf("%s",s); 121 for(int j=1;j<=m<<1;j++) 122 { 123 char ch=s[j-1]; 124 a[i][j]=ch-‘0‘; 125 } 126 } 127 for(int i=1;i<=m;i++) 128 { 129 int sum=0; 130 for(int j=1;j<=n;j++) 131 sum++,b[sum]=a[j][i]; 132 for(int k=n;k>=1;k--) 133 sum++,b[sum]=a[k][i+m]; 134 /*for(int r=1;r<=sum;r++) 135 cout<<b[r]; 136 cout<<endl;*/ 137 int ans=search(); 138 cout<<ans<<endl; 139 } 140 return 0; 141 }
AC代码