2021/7/23

9 篇文章 0 订阅
3 篇文章 0 订阅

链接:https://ac.nowcoder.com/acm/problem/17315
来源:牛客网

Applese有1个容量为v的背包,有n个物品,每一个物品有一个价值ai,以及一个大小bi
然后他对此提出了自己的疑问,如果我不要装的物品装的价值最大,只是一定需要装m个物品,要使得求出来的物品价值的中位数最大
Applese觉得这个题依然太菜,于是他把这个问题丢给了你
当物品数量为偶数时,中位数即中间两个物品的价值的平均值
输入描述:

第一行三个数v, n, m,分别代表背包容量,物品数量以及需要取出的物品数量
接下来n行,每行两个数ai,bi,分别代表物品价值以及大小
n ≤ 1e5, 1 ≤ m ≤ n, ai ≤ 1e9, v ≤ 1e9, bi ≤ v

输出描述:

仅一行,代表最大的中位数

思路:枚举,思维,前缀和,后缀和,优先队列
先对序列排序
你想,我们给定了m个数要找m的中位数,让x=m&1,对于枚举点来说取 (初始i从1开始取) i=m/2-1+x;
前缀维护到当前位置为止一共m/2-1+x个可选择最小的数,比如奇数5,维护前2个,比如偶数6维护前2个。后缀和维护后面m/2个数,比如5维护后面2个,偶数6维护后面3个
对于奇数好办,(i从1开始)从m/2+1开始枚举,到n-m结束,这些都是可以枚举的值,判断s1[i-1]+s2[i+1]+vol[i]<=v,成立就更新答案,选择更大的

对于偶数,我们枚举中间偏左的数。对于右半部分根据单调性二分。由于s2[i+1]存储了i右边符合大小规则和规定长度的物品占用空间的最小值,对右半部分二分。因为对于枚举数确定的情况下,右边那个数越大越好;
s1[i-1]+s2[mid]+vol[i]<=v 说明能够取右边,l=mid+1,不然r=mid-1;
取while循环退出的r值,如果r 在i 的右边,更新。

细节挺多的(那些大佬太强了八/qaq)下面是可读性很强的代码。

#include<bits/stdc++.h>
using namespace std;
int v,n,m;
typedef long long ll;
const int N=1e5+5;
struct node{//由题目后给的数据条件保证了val和vol是正相关的 
	int val;//价值 
	int vol;//占用空间 
};

priority_queue<int>q;

bool cmp(node a,node b){
	return a.val<b.val;//升序排列; 
}

node th[N];

int s1[N],s2[N];//用于记录前缀和后缀和 

void solve(){
	int x=m&1;//判断奇偶
	m>>=1;
	for(int i=1;i<=n;i++){//维护前缀和,序列size=m 
		q.push(th[i].vol);
		s1[i]=s1[i-1]+th[i].vol;
		if(q.size()>m-1+x) s1[i]-=q.top(),q.pop();
	} 
	while(!q.empty()) q.pop(); 
	for(int i=n;i>=1;i--){//维护后缀和 
		s2[i]=s2[i+1]+th[i].vol;
		q.push(th[i].vol);
		if(q.size()>m) s2[i]-=q.top(),q.pop(); 
	}
	if(x){//奇数时直接枚举找最大 
		int ans=0;
		for(int i=m+1;i<=n-m;i++){
			if(s1[i-1]+th[i].vol+s2[i+1]<=v) 
				ans=th[i].val;//中位数 
		}
		cout<<ans<<endl;
	}
	else{
		int ans=0;
		for(int i=m;i<=n-m;i++){//枚举中间偏左的数 
			int l=m+1,r=n-m+1;
			while(l<=r){//二分最后结果取r 
				int mid=(l+r)>>1;
				if(th[i].vol+s1[i-1]+s2[mid]<=v) 
					l=mid+1;
				else
					r=mid-1;
			}
			if(r>i) ans=max(ans,th[i].val+th[r].val);
		}
		cout<< ans/2<<endl;
	}
}



int main(){
	cin>>v>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>th[i].val>>th[i].vol;
	sort(th+1,th+n+1,cmp);	
	solve();
	return 0;
}

链接:https://ac.nowcoder.com/acm/problem/23486
来源:牛客网

小A与小B这次两个人都被困在了迷宫里面的两个不同的位置,而他们希望能够迅速找到对方,然后再考虑如何逃离迷宫的事情。小A每次可以移动一个位置,而小B每次可以移动两次位置,小A移动的方向是上下左右左上左下右上右下8个方向,小B移动的方向是上下左右4个方向,请问他们最早什么时候能够找到对方,如果他们最终无法相遇,那么就输出”NO"。
输入描述:

第一行两个整数N,M分别表示迷宫的行和列。接下来一个N×M的矩阵其中"C"表示小A的位置,“D"表示小B的的位置,”#“表示不可通过的障碍,”."则是可以正常通过的位置。字符用空格隔开第一行两个整数N,M分别表示迷宫的行和列。\
接下来一个N\times M 的矩阵\其中"C"表示小A的位置,"D"表示小B的的位置,\
"#“表示不可通过的障碍,”."则是可以正常通过的位置。\字符用空格隔开\第一行两个整数N,M分别表示迷宫的行和列。接下来一个N×M的矩阵其中"C"表示小A的位置,“D"表示小B的的位置,”#“表示不可通过的障碍,”."则是可以正常通过的位置。字符用空格隔开

输出描述:

如果可以相遇,第一行输出一个YES,第二行一个整数输出最短的相遇时间。
否则就输出一个NO表示不能相遇。

思路:两个起点两个队列,搜索一步步进行,代码是细节.(qaq/代码向大佬学习/瑞思拜);

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int movex[8]={1,-1,0,0,1,-1,-1,1};
int movey[8]={0,0,1,-1,1,1,-1,-1};
int m,n;
const int N=1005;
struct node{
	int x;
	int y;
	node(int a,int b){x=a,y=b;}//注意格式,不然死翘翘
};
bool vis[2][N][N];
char sq[N][N];
queue<node>q[2];
bool stepbfs(int x){
	int cnt=q[x].size();
	while(cnt--){
		node head=q[x].front();
		q[x].pop();
		for(int i=0;i<8-4*x;i++){
			int nx=head.x+movex[i];
			int ny=head.y+movey[i];
			if(nx<0||nx>=n||ny<0||ny>=m||sq[nx][ny]=='#')
				continue;
			if(!vis[x][nx][ny]){
				vis[x][nx][ny]=true;
				q[x].push(node(nx,ny));
            if(vis[1-x][nx][ny])
				return true;
			}
		}
	}
	return false;
}
int  solve(){
	int cnt=0;
	while(!q[0].empty()||!q[1].empty()){
		cnt++;
		if(stepbfs(0)) return cnt;
		if(stepbfs(1)) return cnt;
		if(stepbfs(1)) return cnt;
	}
	return -1;
}
int main(){
	cin>>n>>m;
	memset(vis,false,sizeof(vis));
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			getchar();
			sq[i][j]=getchar();
			if(sq[i][j]=='C'){
				vis[0][i][j]=true;
				q[0].push(node (i,j));
			}
			if(sq[i][j]=='D'){
				vis[1][i][j]=true;
				q[1].push(node (i,j));
			}
		}
	}
	int ans=solve();
	if(ans==-1){
		cout<<"NO\n";
	}
	else{
		cout<<"YES\n"<<ans<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值