【二分】P1462 通往奥格瑞玛的道路

前置芝士:二分

[P1182]

对于给定的一个长度为N的正整数数列 \(A_{1 \sim N}\),现要将其分成 \(M (M≤N)\)段,并要求每段连续,且每段和的最大值最小。

眼神告诉我,每段和的最大值满足:

  1.有上下界。
  2.有单调性。

求最值考虑二分。
每段和的最大值上界即为所有数之和,下界为数列元素的最大值。
二分到x后check(x),计算若每段之和不大于x可以分多少段,若大于等于m则向上二分,否则向下二分。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m,a[maxn];
int l,r,tot,cnt;
bool judge(int x,int a[]){
	for(int i=0;i<n;i++){
		if(tot+a[i]<=x)tot+=a[i];
		else tot=a[i],cnt++;
	}
	return cnt>=m;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
		r+=a[i];
		l=l>a[i]?l:a[i];
	}
	while(l<=r){
		int mid=l+r>>1;
		tot=cnt=0;
		if(judge(mid,a))l=mid+1;
		else r=mid-1;
	}
	printf("%d",l);
	return 0;
}



P1462 (dij+二分)

同理,交费的最大值上界为F数组的最大值,下界不确定(没有限制,就用1吧)。
然后以二分到的x为最大值check,判断是否有一条路可以使得每条边的收费都小于等于此值并且走到终点之后血量不会被扣光。

#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
#define ll long long
ll f[maxn],dis[maxn],b,v;
vector<pair<int,ll> >vec[maxn];
priority_queue<pair<ll,int> >q;
int vis[maxn],n,m,x,y;

int check(int x){
	if(f[1]>x)return 0;
	for(int i=1;i<=n;i++)dis[i]=1e18,vis[i]=0;
	dis[1]=0;
	q.push(make_pair(0,1));
	while(!q.empty()){
		int u=q.top().second;
		q.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(int i=0;i<vec[u].size();i++){
			int v=vec[u][i].first;
			if(f[v]>x)continue;
			ll w=vec[u][i].second;
			if(dis[u]+w<dis[v]){
				dis[v]=dis[u]+v;
				q.push(make_pair(-dis[v],v));
				if(v==n){
					if(dis[n]>=b)return 0;
					else return 1;
				}
			}
		}
	}
	return 0;
}

int main(){
	ll mxx=-1;
	scanf("%d%d%lld",&n,&m,&b);
	for(int i=1;i<=n;i++){
		scanf("%lld",&f[i]);
		mxx=max(mxx,f[i]);
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d%lld",&x,&y,&v);
		vec[x].push_back(make_pair(y,v));
		vec[y].push_back(make_pair(x,v));
	}
	ll ans=-1,l=1,r=mxx;
	while(l<=r){
		ll mid=l+r>>1;
		if(check(mid))ans=mid,r=mid-1;
		else l=mid+1;
	}
	if(ans==-1)puts("AFK");
	else printf("%lld\n",ans);
	return 0;
}
posted @ 2020-10-09 19:46  zdxx  阅读(99)  评论(0编辑  收藏  举报