标签:ret 应该 cstring name uvalive 硬币 ring 查找 std
题目链接:https://vjudge.net/contest/241341#problem/I
题目大意:输入t,t组样例,输入n,m,有n个圆槽,m个硬币,接下来m行代表每个硬币所在的位子,要求你移动硬币使得相邻的硬币距离相等,输出最小的最大移动步数(这里所指的是
一个硬币最大移动的步数)
个人思路:一直在找规律,后来也没有找到,应该是没有规律吧,搜题解也没有搜到有什么规律,全都是用二分来求的。。。这里怎么用二分呢?
因为数据范围是n<=1e6,并且如果通过单点最大k步移动可以是这些点两两间隔n/m,那么对于任意的k’(k’>k)也一定是可以的。所以可以二分查找需要的最小单点最大移动步数。当单点移动最大步数确定为d时如何判断d是否能使得这些点均匀分布呢?
考虑每个点的移动区间,假设第i-1个点的移动区间是[prelow,prehigh],因为第i点最多可移动d步,所以第i个点的移动区间是[data[i]-d,data[i]+d],又因为第i-1个点和第i个点需要间隔step=n/m,所以第i-1个点给第i个点的约束移动区间是[prelow+step,prehigh+step],所以第i个点的实际允许移动区间是
curlow=max(prelow+step,data[i]-d), curhigh=min(prehigh+step,data[i]+d);
只要对每个点判断这个区间是否存在即curlow<=curhigh是否成立,就能判断d是否合法了。
看代码
#include<iostream> #include<cstdio> #include<cstring> #include<stdio.h> #include<string.h> #include<cmath> #include<math.h> #include<algorithm> #include<set> #include<queue> #include<map> typedef long long ll; using namespace std; const ll mod=1e9+7; const int maxn=2e4+10; const int maxk=100+10; const int maxx=1e4+10; const ll maxe=1000+10; #define INF 0x3f3f3f3f3f3f int a[maxn]; int n,m,step; bool solve(int mid) { int prelow,prehigh,curlow,curhigh; prelow=a[0]-mid; prehigh=a[0]+mid; for(int i=1;i<m;i++) { curlow=max(prelow+step,a[i]-mid); curhigh=min(prehigh+step,a[i]+mid); if(curlow>curhigh) return false; prelow=curlow; prehigh=curhigh; } return true; } int main() { int t,sum=1; cin>>t; while(t--) { cin>>n>>m; for(int i=0;i<m;i++) { cin>>a[i]; } sort(a,a+m); int l=0,r=n; step=n/m; while(l<r) { int mid=(l+r)/2; if(solve(mid)) { r=mid; } else l=mid+1; } printf("Case #%d: %d\n",sum++,r); // cout<<r<<endl; } }
标签:ret 应该 cstring name uvalive 硬币 ring 查找 std
原文地址:https://www.cnblogs.com/caijiaming/p/9386050.html