2020年牛客算法入门课练习赛3 (全部题解)

题目连接

A 胖胖的牛牛

题解:用优先队列实现bfs,保存五个信息,当前坐标的x,y值;上一步的坐标值,pre_x,pre_y;从起点到这个点一共转了多少次方向val。那么如果当前的x,y与他前一步的前一步的x,y都不相等,代表转了一个方向,更新val,加入到优先队列中,如果执行完bfs还没有到终点输出-1即可

#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;

const int maxn = 1e3+10;
char a[maxn][maxn];
struct node {
    
    
	int prex,prey,x,y,c;
	bool operator < (const node & t)const {
    
    
		return c > t.c;
	}
};

int xx[] = {
    
    -1,0,1,0};
int yy[] = {
    
    0,-1,0,1};
map<pi,int>mp;

int main()
{
    
    
	int n,sx,sy,fx,fy;
	cin >> n;
	for(int i=1;i<=n;i++)
	{
    
    
		for(int j=1;j<=n;j++)
		{
    
    
			cin >> a[i][j];
			if(a[i][j] == 'A')
			{
    
    
				sx = i,sy = j;
			}
			else if(a[i][j] == 'B')
			{
    
    
				fx = i,fy = j;
			}
		}
	}
	
	priority_queue<node>q;
	q.push(node(sx,sy,sx,sy,0));
	mp[mk(sx,sy)] = 1;
	
	while(!q.empty())
	{
    
    
		node tmp = q.top();
		q.pop();
		int x = tmp.x;
		int y = tmp.y;
		int prex = tmp.prex;
		int prey = tmp.prey;
		int c = tmp.c;
		if(x == fx && y == fy)
		{
    
    
			printf("%d\n",c);
			return 0;
		}
		for(int i=0;i<4;i++)
		{
    
    
			int tx = x + xx[i];
			int ty = y + yy[i];
			if(a[tx][ty] == 'x' || mp[mk(tx,ty)] || tx < 1 || tx > n || ty < 1 || ty > n)continue;
			mp[mk(x,y)] = 1;
			int flag;
			if(tx == prex || ty == prey)flag = 0;
			else flag = 1;
			q.push(node(x,y,tx,ty,c+flag));
		}
	}
	puts("-1");
	return 0;
}

B 牛牛的零食

题解:容斥定理,因为n只有15,我们可以用二进制来枚举状态,我们要求的是 l l l r r r区间中能被8整除,并且不能被数组中的任何一个数整除,我们先求出能被8整除的数有多少,然后减去能被8整除又能被数组中某些数整除的输的个数。那么如何求能被8整除又能被数组中某些数整除呢?我们求出用二机制表示的集合中的这些数的最小公倍数,与8再取一次lcm,拿区间右端点除以这个lcm。但是同一个区域可能减去多次,这时我们就要用到容斥了,单数就减,双数就加。

#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;
ll a[25];
int n;
ll cal(int x)
{
    
    
    ll ans = x/8;
     
    for(int s=1;s<1<<n;s++)
    {
    
    
        int cnt = 0;;
        ll sum = 8,flag = 0;
        for(int i=0;i<n;i++)
        {
    
    
            if(s&(1<<i))
            {
    
    
                ++cnt;
                sum = sum/__gcd(sum , a[i+1])*a[i+1];
                if(sum > x)
                {
    
    
                    flag = 1;
                    break;
                }
            }
        }
        if(flag == 0)
        {
    
    
            if(cnt&1)ans -= x/sum;
            else ans += x/sum;
        }
    }
    return ans;
}
int main()
{
    
    
    cin >> n;
    for(int i=1;i<=n;i++)cin >> a[i];
    int l,r;
    cin >> l >> r;
    cout<<cal(r)-cal(l-1)<<'\n';
    return 0;
}

C 牛牛的最美味和最不美味的零食

题解:线段树,查询区间最大值最小值用最大值最小值线段树即可,那么如何实现删除一个数,然后查询呢?我们可以另外开一颗线段树,保存这个位置是否有值,那么查询第 l , r l,r l,r个数原来的位置就等于查询区间第 k k k个数,类似于权值线段树。

#include <bits/stdc++.h>
#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;

#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define ls o<<1
#define rs o<<1|1
#define mid (l+r)/2
const int maxn = 4e6+10,inf = 0x3f3f3f3f;
int tr[maxn],mi[maxn],ma[maxn];
void mer(int o)
{
    
    
	tr[o] = tr[ls] + tr[rs];
	mi[o] = min(mi[ls],mi[rs]);
	ma[o] = max(ma[ls],ma[rs]);
}
int x;
void build(int o,int l,int r)
{
    
    
	if(l == r)
	{
    
    
		scanf("%d",&x);
		ma[o] = mi[o] = x;
		tr[o] = 1;
		return ;
	}
	build(lson);
	build(rson);
	mer(o);
}
void ups(int o,int l,int r,int p)
{
    
    
	if(l == r)
	{
    
    
		tr[o] = 0;
		mi[o] = inf;
		ma[o] = -inf;
		return ;
	}
	if(p <= mid)ups(lson,p);
	else ups(rson,p);
	mer(o);
}

int quId(int o,int l,int r,int k)
{
    
    
	if(l == r)return l;
	if(tr[ls] >= k)return quId(lson,k);
	else return quId(rson,k-tr[ls]);
}

int quma(int o,int l,int r,int L,int R)
{
    
    
	if(l >= L && r <= R)return ma[o];
	int ans = -inf;
	if(L <= mid)ans = max(ans,quma(lson,L,R));
	if(R > mid)ans = max(ans,quma(rson,L,R));
	return ans;
}
int qumi(int o,int l,int r,int L,int R)
{
    
    
	if(l >= L && r <= R)return mi[o];
	int ans = inf;
	if(L <= mid)ans = min(ans,qumi(lson,L,R));
	if(R > mid)ans = min(ans,qumi(rson,L,R));
	return ans;
}
int main()
{
    
    
	int n,m;
	scanf("%d%d",&n,&m);
	build(1,1,n);
	while(m--)
	{
    
    
		int op,l,r;
		scanf("%d%d",&op,&l);
		if(op == 1)
		{
    
    
			l = quId(1,1,n,l);
			ups(1,1,n,l);
		}
		else
		{
    
    
			scanf("%d",&r);
			l = quId(1,1,n,l);
			r = quId(1,1,n,r);
			printf("%d %d\n",qumi(1,1,n,l,r),quma(1,1,n,l,r));
		}
	}
	return 0;
 } 

D 瘦了的牛牛去旅游

题解: f l o y d floyd floyd的变形,我们知道弗洛伊德算法思想是 d p [ k ] [ i ] [ j ] dp[k][i][j] dp[k][i][j]代表经过编号不超过k 的若干点作为中转点,i到j的最短路, d p [ k ] [ i ] [ j ] = m i n ( d p [ k ] [ i ] [ j ] , d p [ k − 1 ] [ i ] [ k ] + d p [ k − 1 ] [ k ] [ j ] ) dp[k][i][j] = min(dp[k][i][j] , dp[k-1][i][k] + dp[k-1][k][j]) dp[k][i][j]=min(dp[k][i][j],dp[k1][i][k]+dp[k1][k][j]),一般我们省掉第一维,那么我们可以添加一个维度, d p [ p ] [ k ] [ i ] [ j ] dp[p][k][i][j] dp[p][k][i][j]代表经过i到j经过p条边的最短路, d p [ p ] [ i ] [ j ] = m i n ( d p [ p ] [ i ] [ j ] , d p [ p − 1 ] [ i ] [ k ] + d p [ 1 ] [ k ] [ j ] ) dp[p][i][j] = min(dp[p][i][j] , dp[p-1][i][k] + dp[1][k][j]) dp[p][i][j]=min(dp[p][i][j],dp[p1][i][k]+dp[1][k][j])

#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;
const int maxn = 55,inf = 0x3f3f3f3f;
 
double ans[maxn][maxn];
int dp[maxn][maxn][maxn];
 
int main()
{
    
    
    int n,m;
    scanf("%d%d",&n,&m);
    memset(dp,0x3f,sizeof(dp));
    for(int i = 1;i <= n;++i){
    
    
        dp[i][i][0]=0;
    }
    for(int i=1;i<=m;i++)
    {
    
    
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        dp[u][v][1] = min(dp[u][v][1],w);
    }
    for(int p=2;p<=n;p++) 
    for(int k=1;k<=n;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++) {
    
    
        dp[i][j][p] = min(dp[i][j][p] , dp[i][k][p-1] + dp[k][j][1]);
    }
     
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    {
    
    
        if(i == j)continue;
        ans[i][j] = inf;
        for(int p=1;p<=n;p++)
            if(dp[i][j][p]<inf){
    
    
            ans[i][j] = min(ans[i][j] , 1.0*dp[i][j][p]/p);
        }
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
    
    
        int l,r;
        scanf("%d%d",&l,&r);
        if(inf != ans[l][r])printf("%.3lf\n",ans[l][r]);
        else puts("OMG!");
    }
    return 0;
}

E 只能吃土豆的牛牛

题解:dfs,我们另原数组为:30,31,32…,我们可以简单的列出新数组前几项,1,3,1+3,9,1+9,3+9,1+3+9,27…,我们发现新生成的数组,每个在原数组中有的数比如3,9后面都跟着他在新数组中的下标减一个数,因为这个数可以跟他前面的每个数组成一个新的数。那么我们就可以递归写,dfs(n)代表第n个数是多少,具体看代码:

#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;

ll dfs(int n)
{
    
    
	if(n == 0)return 0;
	if(n == 1)return 1;
	if(n == 2)return 3; 
	ll sum = 1;
	for(int i=1;;i++)
	{
    
    
		sum++;
		sum += (sum - 1);
		if(sum >= n)
		{
    
    
			sum = (sum + 1)/2;
			return pow(3,i) + dfs(n-sum);
		}
	}
}
int main()
{
    
    
	int T,kase = 0;
	cin >> T;
	while(T--)
	{
    
    
		int n;
		cin >> n;
		printf("Case #%d: %lld\n",++kase,dfs(n));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44499508/article/details/107371785