在求最值問題裏,我們最常見的方法是動態規劃,回朔法。然而二分思想也是能夠解決某一類最值問題,而且這一類最值問題會用明顯的特徵。
先來看題目hiho第38周題目:http://hihocoder.com/contest/hiho38/problem/1
將該題目稍微簡化模型,意思就是求點到點的最長路徑邊的最小值。該題目是不能用動態規劃的,因爲並不存在動態轉移方程。因此只能用回朔法。但是這類題目有明顯的特徵就是:我們一個確定2個值,left_value,right_value,left_value一定不滿足題目要求,而right_value則一定滿足題目要求。故我們要找的最小值一定是在區間[left_value,right_value]內,一點有序區間去尋找某個值就可以用二分的思想。該替思路如下:
其中打紅色方框的就是該類型題目的共性,不同的則是需要根據題意,編寫不同的f(x)函數。
該題源代碼(已accept)如下:
#include<cstdio>
#include<cstring>
#include<queue>
#include<utility>
#include<vector>
#include<algorithm>
#define PMAXN 10010
#define GMAXN 200010
using namespace std;
struct edge
{
int id;
int next;
int value;
// edge(int value,int _id=0,int _next=-1):id(_id),next(_next){}
};
int pnode[PMAXN]; //用來存放點
edge lines[GMAXN]; //用來存放邊
bool vis[PMAXN];
int lines_index=0; //邊數組的索引下標
int n,m,k,t;
int key[GMAXN]; //用來存放邊權
int key_index=0;
void add(int l,int r,int value)
{
lines[lines_index].id=l;
lines[lines_index].next=pnode[r];
lines[lines_index].value=value;
pnode[r]=lines_index++;
lines[lines_index].id=r;
lines[lines_index].next=pnode[l];
lines[lines_index].value=value;
pnode[l]=lines_index++;
}
//不能使用深搜,會超時,因爲深搜會使用遞歸,數據太大真不好用
bool f(int x)
{
memset(vis,false,sizeof(vis));
queue<pair<int,int> > q;
q.push(make_pair(1,0));
vis[1]=true;
while(!q.empty())
{
int node=q.front().first;
int num=q.front().second;
if(num>=k)
return false;
q.pop();
for(int i=pnode[node];i!=-1;i=lines[i].next)
{
if(lines[i].value>x||vis[lines[i].id])
continue;
if(lines[i].id==t)
return true;
q.push(make_pair(lines[i].id,num+1));
vis[lines[i].id]=true;;
}
}
return false;
}
int BinaryAnswer(int left,int right)
{
if(left==right-1)
return right;
int mid=(left+right)/2;
if(f(key[mid]))
return BinaryAnswer(left,mid);
else
return BinaryAnswer(mid,right);
}
int main()
{
freopen("a.txt","r",stdin);
scanf("%d%d%d%d",&n,&m,&k,&t);
for(int i=0;i<=n;i++)
pnode[i]=-1;
for(int i=0;i<m;i++)
{
int l,r,value;
scanf("%d%d%d",&l,&r,&value);
key[key_index++]=value;
add(l,r,value);
}
sort(key,key+key_index);
int *p=unique(key,key+key_index);
key_index=p-key;
if(f(key[0]))
printf("%d",key[0]);
else
printf("%d",key[BinaryAnswer(0,key_index-1)]);
return 0;
}
來看一到hiho上微軟面試題,同樣使用的是二分搜索。 http://hihocoder.com/problemset/problem/1095
源代碼(已accept)如下:
#include<cstdio>
#define maxn 100010
int n,k;
int d[maxn]; //每一次的隨機數
int hi,ho,ho_cup;
//true表示小ho贏
bool f(int t)
{
hi=0,ho=0;
int ho_cup=t;
for(int i=0;i<n;i++)
{
if(ho_cup<=d[i])
{
hi++;
ho_cup=0;
}
else
{
ho++;
ho_cup-=d[i];
}
ho_cup+=t;
}
return ho>hi;
}
int BinarySearch(int l,int r)
{
if(l==r-1)
return r;
int mid=(l+r)/2;
if(f(mid))
return BinarySearch(l,mid);
else
return BinarySearch(mid,r);
}
int main()
{
freopen("a.txt","r",stdin);
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
scanf("%d",d+i);
printf("%d",BinarySearch(0,k+1));
return 0;
}