牛客周赛 Round 33(A,B,C,D,E,F)

最近比赛好多啊,打不完了。这场的题比较骗人,真实难度实际上不高,就是想不到

比赛链接


A 小红的单词整理

code:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

string s1,s2;

int main(){
    cin>>s1>>s2;
    cout<<s2<<endl<<s1;
    return 0;
}

B 小红煮汤圆

思路:

算出前 i i i 次煮汤圆总共需要拆开的袋数,再算出前 i − 1 i-1 i1 煮汤圆总共需要拆开的袋数,它们的差值就是第 i i i 次需要拆开的袋数。

i i i 次煮汤圆需要煮汤圆需要 i ∗ k i*k ik 个,假设最少要拆了 t t t 袋,那么会拆出来 t ∗ x t*x tx 个汤圆。根据定义可以得出不等式: ( t − 1 ) ∗ x < i ∗ k ≤ t ∗ x (t-1)*x\lt i*k\le t*x (t1)x<iktx t − 1 < i ∗ k x ≤ t t-1\lt \frac {i*k}x \le t t1<xikt t = ⌈ i ∗ k x ⌉ t=\left\lceil \frac {i*k}x\right\rceil t=xik

code:

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

int n,x,k;

int main(){
	cin>>n>>x>>k;
	int m;
	cout<<(m=n*x/k)<<endl;
	for(int i=1,t1,t2;i<=m;i++){
		t1=(i*k+x-1)/x;
		t2=((i-1)*k+x-1)/x;
		cout<<t1-t2<<" ";
	}
	return 0;
}

C 小红的 01 串

思路:

考虑如何删除,如果前面有前导零肯定删掉,如果没有 1 1 1 就是把串删完,结果就是0。有 1 1 1 就一定会遇到 1 1 1。我们会保留这个 1 1 1 之后只用操作 2 2 2 删除第二个字符,这时可以把第一个字符直接忽略,我们的操作 2 2 2 相当于是在删除剩余字符串的第一个字符。所以我们可以先看一下串里有没有 1 1 1,没有就返回 0 0 0,否则把第一个 1 1 1 之前的部分删掉。之后再在剩余的字符串上不停删除第一个字符看 01 01 01 差值的最大值是多少。

可以算出 01 01 01 差值的前缀和 s s s,然后用 s [ n ] − m i n { s [ i ] } s[n]-min\{s[i]\} s[n]min{s[i]} 就行了。

#include <iostream>
#include <cstdio>
#include <cstdio>
using namespace std;
const int maxn=1e5+5;

int n,s[maxn];
string str;

int main(){
	cin>>str;
	if(str.find_first_of("1")==string::npos){
		cout<<0<<endl;
		return 0;
	}
	str=" "+str.substr(str.find_first_of("1")+1);
	n=str.length()-1;
	int minn=0;
	for(int i=1;i<=n;i++){
		s[i]=s[i-1]+((str[i]=='0')?-1:1);
		minn=min(minn,s[i]);
	}
	cout<<s[n]-minn+1<<endl;
	return 0;
}

D 小红的数组清空

思路:

由于元素可以重复,所以不能排序后找公差为 1 1 1 递增的等差数列。

这个题直接模拟删数过程:先删掉剩余数中最小的数 x x x,如果存在一个 x + 1 x+1 x+1,就顺便删掉,再找 x + 2 x+2 x+2,类推,没有就重新选数删掉。

code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
const int maxn=1e5+5;

int n;
map<int,int> mp;

int main(){
	cin>>n;
	for(int i=1,t;i<=n;i++){
		cin>>t;
		mp[t]++;
	}
	int ans=0;
	while(!mp.empty()){
		auto it=mp.begin();
		int x=it->first;
		ans++;
		if(--it->second==0)mp.erase(it);
		while(mp.count(x+1)){
			x++;
			mp[x]--;
			if(mp[x]==0)mp.erase(x);
		}
	}
	cout<<ans;
	
	return 0;
}

E 小红勇闯地下城

思路:

一眼看上去像DP,但是起点和终点不在角上,而且移动可以绕圈。

考虑到我们到达某个位置的时候,一定是血量更多是更好的,而不关心我们具体是怎么到达这个位置的。而我们的血量是不断减少的,中间不会回血。这意味着如果我们从所有状态中选出血量最多的状态,如果这个状态所在位置之前还没有被访问过,那么现在这个状态的血量就是最优的,我的意思是,之后到达这个位置的最大血量就再也不可能被刷新了。

证明是显然的:现在所有状态的血量都少于这个状态,我们的血量还是不断减少的,无论从什么状态来推,之后到达的时候血量一定不可能大于现在的血量了。

这有堆优化dijkstra那味了。所以我们这里使用堆来代替队列进行BFS(或者说是堆优化dijkstra也没有问题),某个位置的最大血量不可能被刷新就代表这个位置之后就不可能入堆了,所以我们相当于每从堆中取出一个新点,就确定好了一个点。因为每个点都只会拿来拓展一次,拓展一次要检查四个点,所以总的时间复杂度大概是 O ( 4 n ∗ m ∗ l o g ( n ∗ m ) ) O(4n*m*log(n*m)) O(4nmlog(nm))

需要动态开空间。

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;

int T,n,m,hp;
struct state{
	int x,y,hp;
	bool operator<(const state x)const{return hp<x.hp;}
	state(int x,int y,int hp):x(x),y(y),hp(hp){};
};

int fx[]={1,-1,0,0},fy[]={0,0,1,-1};

int main(){
	cin>>T;
	while(T--){
		cin>>n>>m>>hp;
		vector<vector<int> > vis(n+1,vector<int>(m+1,0));//到达(x,y)位置的最大血量
		vector<vector<int> > mp(n+1,vector<int>(m+1));
		priority_queue<state> q;
		
		int sx,sy,ex,ey;
		for(int i=1;i<=n;i++){
			string s;
			cin>>s;
			s=" "+s;
			for(int j=1;j<=m;j++){
				if(s[j]=='S'){
					sx=i;
					sy=j;
					mp[i][j]=0;
				}
				else if(s[j]=='T'){
					ex=i;
					ey=j;
					mp[i][j]=0;
				}
				else mp[i][j]=s[j]-'0';
			}
		}
		
		bool flag=false;
		q.push(state(sx,sy,hp));
		vis[sx][sy]=hp;
		while(!q.empty()){
			int ux=q.top().x,uy=q.top().y,hp=q.top().hp;
			q.pop();
            if(hp>vis[ux][uy])continue;
//			printf("(%d,%d) hp=%d\n",ux,uy,hp);
			if(ux==ex && uy==ey){
				flag=true;
				break;
			}
			for(int i=0,x,y;i<4;i++){
				x=ux+fx[i];
				y=uy+fy[i];
				if(x<1 || x>n || y<1 || y>m)continue;
				if(hp-mp[x][y]>vis[x][y]){
					vis[x][y]=hp-mp[x][y];
					q.push(state(x,y,vis[x][y]));
				}
			}
		}
		
		puts((flag)?"Yes":"No");
	}
	return 0;
}

F 小红的数组操作

思路:

这题相当于单调栈维护的贪心。具体可以看这个大佬的讲解,有点忙就不多赘述了。

听说出题人说是斜率优化,但是直播录播和题解我都没看到,等有了再研究研究,如果有会的可以踢我一脚。

code:

#include <iostream>
#include <cstdio>
#include <stack>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
const ll inf=1e9;

int n;
ll a[maxn],s[maxn],nw[maxn];

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i]+=inf;
		a[i]-=i;
		s[i]=s[i-1]+a[i];
	}
	
	stack<pair<ll,int> > s;
	for(int i=1;i<=n;i++){
		pair<ll,int> x(a[i],1),t;
		ll tot=a[i],len=1,ave=a[i],t1,t2;
		while(!s.empty() && s.top().first>=ave){
			t=s.top();
			s.pop();
			tot+=t.first*t.second;
			len+=t.second;
			ave=tot/len;
		}
		t2=tot-ave*len;
		t1=len-t2;
		s.push(make_pair(ave,t1));
		if(t2)s.push(make_pair(ave+1,t2));
	}
	int p=n;
	while(!s.empty()){
		for(;s.top().second;s.top().second--){
			nw[p--]=s.top().first;
		}
		s.pop();
	}
	
	ll ans=0;
    for(ll i=1,dt;i<n;i++){
        if(a[i]>nw[i]){
            dt=a[i]-nw[i];
            a[i]-=dt;
            a[i+1]+=dt;
            ans+=dt;
        } 
    } 
	for(int i=1;i<=n;i++)
		nw[i]+=i-inf;
	
	cout<<ans<<endl;
	for(int i=1;i<=n;i++)
		cout<<nw[i]<<" ";
	return 0;
} 
  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牛客 a卷2022年第四季度的华为题目中,要求考生设计一种高效的数据结构,能够支持以下几种操作: 1. 添加一个元素 2. 删除一个元素 3. 查找是否存在某个元素 4. 返回元素的总数 该数据结构要求满足空间复杂度较小、时间复杂度较低、能够快速地进行查找和修改等多种操作。 想要编写这样一种数据结构,我们可以参考许多已有的经典算法与数据结构,如二叉树、哈希表、红黑树等,通过综合利用它们的优点来实现这个问题的解决。 例如,我们可以通过哈希表来存储所有元素的值,并在每个哈希链表的元素中再使用红黑树来进行排序与查找。这样,我们既能够轻松地进行元素的添加和删除操作,也能够在查找较大数据范围和数量时保持较高的速度与效率。同时,由于使用了多个数据结构来协同完成这个问题,我们也能够在空间复杂度上适度地进行优化。 当然,在具体设计这个数据结构的过程中,我们还需要考虑一些实践中的细节问题,例如如何避免哈希冲突、如何处理数据丢失与被删除元素所占用的空间等问题,这都需要相应的算法与流程来进行处理。 总体来看,设计这种支持多种操作的高效数据结构,需要我们具备丰富的算法知识和编程实践能力,同时需要我们在具体处理问题时能够将多种算法和数据结构进行有效地结合。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值