题意:奶牛学校招生,n头奶牛报名,要选m头(m为奇数),学校是义务制,所以每头奶牛的学费都由学校负责。每头奶牛都有自己的考试分数和它需要花的学费,学校总共有sum的资金,问合法招生方案中中位数(即排名第(m+1)/2)最高的是多少。
思路:(http://www.cnblogs.com/Thispoet/archive/2011/11/28/2266853.html)二分答案,但是这里的判断不同于一般的二分。因为直接二分不符合单调性。例如下例:
3 5 60
5 100
20 100
30 20
35 20
50 20
此例中,第一次二分的中位数为30,显然无法构成合法的解。但是比他更优的值35却能够构成合法的解。所以要改变二分的策略。
分别将cow按照v(分数)和w(需要的学费)排序。
我的代码里test函数的返回值含义如下:
-1 直接输出,不可能满足条件了
0 这种方案是满足条件的,可以把中位数调大试试
1 这种方案是不满足的,但是把中位数调大就有可能满足了(否则不可能满足)
2 这种方案是不满足的,但是把中位数调小就有可能满足了(否则不可能满足)
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define INF 0x3ffffffffffffff #define clr(s,t) memset(s,t,sizeof(s)) #define N 100005 struct node{ int v,w,id; }s[N]; int cmp1(node a,node b){ return a.v < b.v; } int cmp2(node a,node b){ return a.w < b.w; } int n,m,sum,tmp[N]; int test(int x){ int num,left,right; for(int i = 0;i<n;i++) if(s[i].id == x){ num = s[i].w; break; } left = right = 0; for(int i = 0;i<n;i++){ if(s[i].id<x && num+s[i].w<=sum && left<m/2){ left++; num += s[i].w; }else if(s[i].id>x && num+s[i].w<=sum && right<m/2){ right++; num += s[i].w; } } if(left < m/2 && right<m/2) return -1; else if(left < m/2) return 1; else if(right < m/2) return 2; return 0; } int main(){ int i,j,res=-1,low,high,mid; scanf("%d %d %d",&m,&n,&sum); for(i = 0;i<n;i++) scanf("%d %d",&s[i].v,&s[i].w); sort(s,s+n,cmp1); for(i = 0;i<n;i++){ s[i].id = i; tmp[i] = s[i].v; } sort(s,s+n,cmp2); low = m/2; high = n-m/2-1; while(low <= high){ mid = (low+high)>>1; j = test(mid); if(j==-1){ res = -1; break; } if(j==0){ res = tmp[mid]; low = mid+1; }else if(j==1){ low = mid+1; }else{ high = mid-1; } } if(res==-1) printf("-1\n"); else printf("%d\n",res); return 0; }
2110,给定一个n*n数组,每个位置一个[1,110]之间的数字,求从左上角到右下角的一条路(只能往四邻域行进),使得路径中最大数和最小数的差最小。
思路:二分答案,对每个二分的值mid,还要根据起点的数值进行枚举。然后深搜看看有没有从起点到终点的路径即可。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define INF 0x3fffffff #define clr(s,t) memset(s,t,sizeof(s)) #define N 105 int n; int g[N][N],flag[N][N]; int ori[4][2] = {{-1,0},{0,1},{0,-1},{1,0}}; int check(int x,int y,int f,int d){ return x>0&&y>0&&x<=n&&y<=n&&g[x][y]>=f&&g[x][y]<=f+d&&!flag[x][y]; } int dfs(int x,int y,int f,int d){ int i,j; if(x==n&&y==n) return 1; for(i = 0;i<4;i++){ int xx = x+ori[i][0]; int yy = y+ori[i][1]; if(check(xx, yy, f, d)){ flag[xx][yy] = 1; if (dfs(xx, yy, f, d)) return 1; } } return 0; } int main(){ int i,j,low,high,mid; scanf("%d",&n); for(i = 1;i<=n;i++) for(j = 1;j<=n;j++) scanf("%d",&g[i][j]); low = 0; high = 110; while(low <= high){ mid = (low+high)>>1; for(i = g[1][1]-mid;i<=g[1][1];i++){//枚举路径上数值的上界 clr(flag, 0); flag[1][1] = 1; if(dfs(1,1,i,mid)) break; } if(i>g[1][1]) low = mid+1; else high = mid-1; } printf("%d\n",low); return 0; }