排位赛(九)补题~

Problem B

请添加图片描述
请添加图片描述

题目大意:

给一个n*n的矩阵,由‘.’和‘#’构成,‘.’代表陆地,可以着陆,‘#’代表湖泊,不可以着陆。现有个人要从最左上角跳到最右下角,他一次最多跳k步,问最少步数,若没有,输出-1。

思路:

暴力bfs的话,复杂度是O(n^2*k),限时:2s,肯定是不行的,
但是,我们可以把k优化成logk,通过单调队列维护当前每行每列的最小值,这个最小值指的是从原点跳到该点的最少步数。这样扫一遍矩阵答案就出来了。

AC代码

#include<bits/stdc++.h>
using namespace std;

const int Max = INT_MAX/2;

char mp[2020][2020];
int f[2020][2020];
int n,k;

//单调队列 
struct Queue{
	int t,w,d[2020],id[2020];
	Queue(){
		t=1,w=0;
	}
	void ini(){
		t=1,w=0;
	}
	void add(int v, int p){
		while(t<=w && d[t]>=v) w--;
		d[++w]=v;
		id[w]=p;
	}
	int Find(int p){
		while(t<=w && id[t]<p) t++;
		if(t>w) return Max;
		return d[t];
	}
}q[2020],t;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin>>n>>k;
	
	for(int i=1;i<=n;i++){
		cin>>(mp[i]+1);
	}
	
	int Min;
	f[1][1]=0;
	q[1].add(0,1);
	
	for(int i=1;i<=n;i++){
		t.ini();
		for(int j=1;j<=n;j++){
			if(i==1 && j==1){
				t.add(0, 1);
				continue;
			}
			if(mp[i][j] == '#') continue;
			
			Min=Max;
			
			Min=min(Min, q[j].Find(i-k));
			Min=min(Min, t.Find(j-k));
			
			f[i][j]= (Min == Max ? -1 : Min+1);
			if(Min == Max) continue;
			q[j].add(Min+1, i);
			t.add(Min+1, j);
			
		}
	} 
	
	cout<<f[n][n]<<endl;
	
    return 0;
}

Problem J

请添加图片描述
请添加图片描述

题目大意:

有A,B,C三类选手,他们有能力值a,b,c;他们参加双人划桨比赛,船有好有坏,用质量衡量。能力值为v1,v2的选手划质量为m的船,船速为m*(v1+v2)。问选手和船应怎样搭配,能使最慢的船最快,输出它的速度。

思路:

之前做过相似的,但是没往这方面想呀呀呀o(╥﹏╥)o
二分答案 + 贪心
check答案的时候,用的贪心策略是用最小的代价让当前这条船的船速满足要求。选手的能力值一共6种组合嘛,看看哪种最小且能满足船速要求的就是它了。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;

//答案二分 + 贪心 

int n1,n2,n3;
int v1,v2,v3;
int c[N],sum;

bool chk(int st) {
	
	int t1=n1,t2=n2,t3=n3;
	
	for(int i=1;i<=sum;i++){
		int op=0,mn=1e9;
		
		if(t1>=2 && 2*v1*c[i]>=st && 2*v1<mn) op=1,mn=2*v1;
		if(t2>=2 && 2*v2*c[i]>=st && 2*v2<mn) op=2,mn=2*v2;
		if(t3>=2 && 2*v3*c[i]>=st && 2*v3<mn) op=3,mn=2*v3;
		if(t1>=1 && t2>=1 && (v1+v2)*c[i]>=st && v1+v2<mn) op=4,mn=v1+v2;
		if(t1>=1 && t3>=1 && (v1+v3)*c[i]>=st && v1+v3<mn) op=5,mn=v1+v3;
		if(t2>=1 && t3>=1 && (v2+v3)*c[i]>=st && v2+v3<mn) op=6,mn=v2+v3;
		
		if(!op) return 0;
		
		switch(op){
			case 1 : t1-=2;
			break;
			case 2 : t2-=2;
			break;
			case 3 : t3-=2;
			break;
			case 4 : t1--,t2--;
			break;
			case 5 : t1--,t3--;
			break;
			case 6 : t2--,t3--;
		}
	}
	return 1;
}
int main(){
	cin>>n1>>n2>>n3;
	cin>>v1>>v2>>v3;
	
	sum=(n1+n2+n3)>>1;
	
	for(int i=1;i<=sum;i++){
		cin>>c[i];
	} 
	
//	sort(c+1, c+sum+1);
	
	int l=0,r=1e9,ans;
	while(l<=r){
		int mid = (l+r)>>1;
		
		if(chk(mid)){
			ans=mid;
			l=mid+1;
		}
		else r=mid-1;
		
	}
	cout<<ans<<endl;
	return 0;
} 

Probelm D

请添加图片描述
请添加图片描述

题目大意:

题目的背景是小时候玩的弹珠游戏,珠子从最顶上往下掉,有很多分叉器,珠子可能向左掉也可能往右掉。(也许它有别的名字(ˇˍˇ) 想~)
给一张DAG有向无环图,它的起点为1,终点为0,除此之外其他点的出度均为为2。每个点代表一个转换器(分叉器),它有一个初始状态,决定珠子往哪个方向落,一旦有珠子经过,它的状态就会反转。
数据范围:珠子的数量n达到 1e18 ,点的个数m达到 5e5

思路:

比赛的时候做出来了不过大费周章ε=(´ο`*)))唉
一开始想的是直接DFS,每个点出度为2,复杂度应该是O(2m),可以过~
但是这种情况下,dfs所用的时间会指数爆炸!
既然是DAG,它肯定存在拓扑排序,按着拓扑序进行,排在后面的点一定不会把珠子给到排在前面的点!正解!toposort的是O(m),所以总的时间复杂度为O(2
m)。

AC代码

#include<bits/stdc++.h>
using namespace std;

bool lr[500010];//0是l,1是r
//图 
int ll[500010];
int rr[500010];
long long n,m;
long long v[500010]; 
//入度 
int in[500010];

vector<int> ans;

void topo(){
	queue<int> q;
	for(int i=1;i<=m;i++){
		if(in[i]==0) q.push(i);
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		ans.push_back(u);
		if(--in[ll[u]]==0) q.push(ll[u]);
		if(--in[rr[u]]==0) q.push(rr[u]);
	}
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
		
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		char c;
		int a,b;
		cin>>c>>a>>b;
		if(c=='L') lr[i]=0;
		else if(c=='R') lr[i]=1;
		ll[i]=a;in[a]++;
		rr[i]=b;in[b]++;
	}

	topo();
	
	v[1]=n;
	
	for(auto i : ans){
		if((v[i]&1)==0){
			v[ll[i]]+=v[i]/2;v[rr[i]]+=v[i]/2;
		}
		else{
			if(lr[i]==0){
				lr[i]=1-lr[i];
				v[ll[i]]+=v[i]/2+1;
				v[rr[i]]+=v[i]/2;
			}
			else{
				lr[i]=1-lr[i];
				v[ll[i]]+=v[i]/2;
				v[rr[i]]+=v[i]/2+1;				
			}
		}
	}
	
	for(int i=1;i<=m;i++){
		if(lr[i]==0) cout<<"L";
		else if(lr[i]==1) cout<<"R";
	}
	
	cout<<'\n';
	
	return 0;
} 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

u小鬼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值