链接: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;
}