P1902 刺杀大使 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
二分答案最明显的特征为:需要求的东西最大值最小或者是最小值最大,对于这一类题目,大多都为二分答案。二分答案和暴力相比,二分答案把线性的时间复杂度变成了logn级别,从而保证整个题目的时间复杂度为nlogn,不会超时。
二分答案的模板:
int mid;
int ans=0;
while(l<=r){
mid=(l+r)/2;
if(check(mid)){
ans=mid;
l=mid+1;//此处不一定是这样,要根据题目具体进行判断
}
else{
r=mid-1;//此处同理
}
}
二分的板子在市面上有很多种,笔者觉得这种可能更容易记忆,相较于while的循环为(l<r)这种,循环中到底是l-1还是r-1,个人觉得太难记忆了。再来说说此处ans的作用,跟普通的二分答案板子不同,市面上大多数二分答案的板子最后都是输出l,r,mid三者之一,那么此处的ans就是保证ans记录的最后一个合法的值,比如一道题目,可能我最后结束的mid不一定合法,倒数第二个mid是合法的,而此处的ans一定会去记录倒数第二个mid,而不会记录第二个mid。这么讲解可能有点抽象,到相应的题目自己debug一下就很快就会明白ans的精妙之处。
二分答案的做法:题目要求我们求什么东西,我们就对什么进行二分,至于其他的条件,都是在check函数中使用。
上述都是代码模板,而二分答案的精髓则在于模板中的check函数,对于check函数,没有什么技巧,只能把题目给出的信息当作对于check的限制条件,然后写出相应的代码。对于本题而言,显而易见,check函数肯定是跟搜索有关的代码,一开始写的dfs代码tle了,于是换成了bfs去搜然后就过了,写完后看题解,发现bfs也是可以ac的,在bfs的过程中好像不用走回头路即可,即无“恢复现场”这一过程。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=1e9+7;
const int maxv=4e6+5;
int n,m;
int a[1005][1005];
int dx[]={1,-1,0,0};
int dy[]={0,0,-1,1};
int f,tar;
int st[1005][1005];
void bfs(int x,int y)
{
queue<pll> q;
q.push({x,y});
while(!q.empty()){
auto t=q.front();
q.pop();
int zx=t.first,zy=t.second;
if(zx==n) {
f=1;
break;
}
for(int i=0;i<4;i++){
int nx=zx+dx[i],ny=zy+dy[i];
if(st[nx][ny]||nx<1||ny<1||nx>n||ny>m) continue;
if(a[nx][ny]>tar) continue;
q.push({nx,ny});
st[nx][ny]=1;
}
}
}
bool check(int x)
{
f=0;
tar=x;
memset(st,0,sizeof st);
bfs(1,1);
if(f) return true;
else return false;
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) {
cin>>a[i][j];
}
}
int l=0,r=1e9;
int mid;
int ans=0;
while(l<=r){
mid=(l+r)/2;
if(check(mid)){
ans=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
另外此题还有另外一种解法,就是运用最小生成树的相关知识,即找出一条相关路径,让第一行到最后一行联通,而路径上权值的最大值最小。
对最大值最小这句话进行翻译一下,因为题目给出的是点权,所以要满足权值最大,所以将点权转换为边权的过程中,将边权记为两点间点权较大的,之后就是运用kruskral进行维护,在维护过程中记录最大的边权即可。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=1e9+7;
const int maxv=4e6+5;
ll n,m;
int a[1005][1005];
int get(int x,int y)//得到点的编号
{
return (x-1)*m+y;
}
int p[maxv];
struct node
{
int u,v,w;
}e[maxv];
int k;
int find(int x)
{
if(p[x]!=x) return p[x]=find(p[x]);
return p[x];
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
int c=get(i,j);
p[c]=c;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int u=get(i,j);
int v1=get(i+1,j);
int v2=get(i,j+1);//因为是无向边,所以只需要连两条
if(i+1<=n) e[k++]={u,v1,max(a[i][j],a[i+1][j])};
if(j+1<=m) e[k++]={u,v2,max(a[i][j],a[i][j+1])};
}
}
//cout<<k<<endl;
sort(e,e+k,[](node a,node b){
return a.w<b.w;
});
int ans=0;
int st=1,ed=n*m;
for(int i=0;i<k;i++){
int u=e[i].u,v=e[i].v,w=e[i].w;
//cout<<u<<" "<<v<<" "<<w<<endl;
int fu=find(u),fv=find(v);
if(fu!=fv){
p[fu]=fv;
ans=max(ans,w);
}
if(find(st)==find(ed)) break;
}
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}