编程之美2015资格赛 C.基站选址
2015-04-21 20:09
281 查看
这一题看到题目就感觉是三分,然后将基站位于每个格子的总代价打表观察发现,每一行是凹函数or单调减,每一列也是凹函数or单调减,将每一行的最小值取出来组成一行,也是凹函数or单调减。所以用三分嵌套即可。
先对行三分,在计算每一行的最小值时也用三分搜索。
注意这里面可能会爆int,所以要用long long存总代价。
其实我这里面的三分有点问题,对于单调增的数列找到的是第二小的值==,取决于是 if(a[mid]<a[midr]) r=midr;还是if(a[mid]<=a[midr]) r=midr;
不过最后也过了== 改日研究研究一个更好的写法。
偷偷看了一下kuangbin大神的代码,似乎这样的三分可以同时处理凹函数,单调增/减函数。
//int a[]={1,3,6,9,13,14,46,78,88};
//int a[]={88,78,46,14,13,9,6,3,1};
int a[]={88,78,46,1,3,6,9,13,14};
int l=0;
int r=8;
int x1=0;
while(l <= r)
{
int mid1 = l + (r-l)/3;
int mid2 = r - (r-l)/3;
if(a[mid1]<= a[mid2])
{
x1 = mid1;
r = mid2-1;
}
else{
x1 = mid2;
l = mid1+1;
}
}
int ans=min(a[l],a[r]);
cout<<ans<<endl;
//cout<<a[x1]<<" "<<a[l]<<" "<<a[r]<<endl;
先对行三分,在计算每一行的最小值时也用三分搜索。
注意这里面可能会爆int,所以要用long long存总代价。
其实我这里面的三分有点问题,对于单调增的数列找到的是第二小的值==,取决于是 if(a[mid]<a[midr]) r=midr;还是if(a[mid]<=a[midr]) r=midr;
不过最后也过了== 改日研究研究一个更好的写法。
#include<iostream> #include<stdio.h> #include<cstdio> #include<stdlib.h> #include<vector> #include<string> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<queue> #include<ctype.h> #include<map> #include<time.h> #include<bitset> #include<set> #include<list> using namespace std; //BOP quali Pro3 //range of number: //int 2147483647; 10^9 //int -2147483648; //long long 9223372036854775807; 10^18 //long long -9223372036854775808; //sqrt(INT_MAX)=46340 const int maxn=1010; int T; int N; int M; int A; int B; long long x; long long y; pair<long long,long long>user[maxn]; pair<long long,long long>comp[maxn]; long long ans; long long disuser(long long x1,long long y1,long long x2,long long y2) { return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); } long long discomp(long long x1,long long y1,long long x2,long long y2) { return abs(x1-x2)+abs(y1-y2); } long long cal(long long i,long long j) { long long tmp=0; for(int k=0;k<A;k++) { tmp+=disuser(i,j,user[k].first,user[k].second); } long long tmp2=0x3f3f3f3f; for(int k=0;k<B;k++) { tmp2=min(tmp2,discomp(i,j,comp[k].first,comp[k].second)); } tmp+=tmp2; return tmp; } long long trisearch(int row) { //cout<<"r: "<<row<<endl; int l=1; int r=M; while(l<r-1) { int mid=(r+l)/2; int midr=(mid+r)/2; //cout<<mid<<" "<<midr<<endl; // cout<<cal(row,mid)<<" "<<cal(row,midr)<<endl; // system("pause"); if(cal(row,mid)<cal(row,midr)) r=midr; else l=mid; } //cout<<cal(row,l)<<" "<<row<<" "<<l<<endl; return min(cal(row,l),cal(row,r)); } void solve() { int l=1; int r=N; while(l<r-1) { int mid=(r+l)/2; int midr=(mid+r)/2; //cout<<mid<<" "<<trisearch(mid)<<" "<<midr<<" "<<trisearch(midr)<<endl; if(trisearch(mid)<trisearch(midr)) r=midr; else l=mid; //cout<<mid<<" "<<midr<<endl; } ans=min(trisearch(l),trisearch(r)); } void test() { for(int i=1;i<=N;i++) { // int aa=0x3f3f3f3f; for(int j=1;j<=M;j++) { //if(mp[i][j]!=0) continue; long long tmp=0; for(int k=0;k<A;k++) { tmp+=disuser(i,j,user[k].first,user[k].second); } long long tmp2=0x3f3f3f3f; for(int k=0;k<B;k++) { tmp2=min(tmp2,discomp(i,j,comp[k].first,comp[k].second)); } tmp+=tmp2; ans=min(ans,tmp); // aa=min(aa,tmp); //cout<<tmp<<", "; } //cout<<aa<<endl; // cout<<endl; } } int testtri() { int a[26]={10397, 9742, 9135, 8576, 8065, 7604, 7191, 6826, 6509, 6240, 6019, 5846, 5721, 5643, 5612, 5629, 5694, 5807, 5968, 6177, 6436, 6743, 7098, 7501, 7952, 8451}; //int a[13]={99,88,77,66,55,44,33,32,31,10,8,6,1}; //int a[13]={0,1,3,5,6,7,9,11,14,16,17,23,25}; int l=1; int r=12; while(l<r-1) { int mid=(r+l)/2; int midr=(mid+r)/2; if(a[mid]<a[midr])// can not deal with ascending array { r=midr; } else l=mid; cout<<mid<<" m "<<midr<<endl; } cout<<a[l]<<" "<<a[r]<<endl; return a[l]<a[r]?a[l]:a[r]; } int main() { scanf("%d",&T); for(int ca=1;ca<=T;ca++) { scanf("%d %d %d %d",&N,&M,&A,&B); memset(user,0,sizeof(user)); memset(user,0,sizeof(comp)); ans=0x3f3f3f3f; for(int i=0;i<A;i++) { scanf("%lld %lld",&x,&y); user[i]=make_pair(x,y); } for(int i=0;i<B;i++) { scanf("%lld %lld",&x,&y); comp[i]=make_pair(x,y); } solve(); printf("Case #%d: %lld\n",ca,ans); } return 0; }
偷偷看了一下kuangbin大神的代码,似乎这样的三分可以同时处理凹函数,单调增/减函数。
//int a[]={1,3,6,9,13,14,46,78,88};
//int a[]={88,78,46,14,13,9,6,3,1};
int a[]={88,78,46,1,3,6,9,13,14};
int l=0;
int r=8;
int x1=0;
while(l <= r)
{
int mid1 = l + (r-l)/3;
int mid2 = r - (r-l)/3;
if(a[mid1]<= a[mid2])
{
x1 = mid1;
r = mid2-1;
}
else{
x1 = mid2;
l = mid1+1;
}
}
int ans=min(a[l],a[r]);
cout<<ans<<endl;
//cout<<a[x1]<<" "<<a[l]<<" "<<a[r]<<endl;
相关文章推荐
- 编程之美2015资格赛 题目3 : 基站选址
- 2015编程之美资格赛:基站选址 暴力
- 编程之美2015资格赛 - 题目3 : 基站选址(三分)
- hiho 编程之美2015资格赛(基站选址-绝对值方程分段)[WA]
- 【编程之美2015资格赛】基站选址(数学题)
- 2015编程之美资格赛题目3 : 基站选址
- 基站选址(编程之美2015资格赛)
- 2015编程之美资格赛 C 基站选址
- 编程之美2015资格赛 C 基站选址 (数学)
- hihocoder 2015编程之美 资格赛 hihocoder 第三题 基站选址
- 2月29日(编程之美2015资格赛)
- 【又见C#】CH常用战略回忆 _CodeHunt战场 @ 编程之美2015资格赛
- 回文字符序列(编程之美2015资格赛)
- 编程之美2015资格赛-题目1 2月29日
- 编程之美2015资格赛
- 编程之美2015资格赛 - 题目1 : 2月29日
- 编程之美2015资格赛 解题报告
- 编程之美2015资格赛
- 2015编程之美(资格赛)--基站选址
- 2015编程之美资格赛题目1 : 2月29日 分类: 算法 2015-04-21 11:28 27人阅读 评论(0) 收藏