华北理工大学ACM协会2021蓝桥杯第三次训练赛题解

目录

  • A. Huffuman树
  • B. 旅行家的预算
  • C. 完美的代价
  • D. 翻硬币
  • E. 扫雷游戏
  • F. 填涂颜色
  • G. 轻重搭配


A. Huffuman树

这题的题干很好懂,即给定一个定长的数组,每次将数组中最小的两个数相加并替换数组中原来的两个元素,直到数组中只剩下一个元素,要求输出每一次相加过程的总和,直接模拟即可。

按照此思路模拟,有一点可以优化。由上面对题目整个过程的描述我们可以很容易想到,当数组中只剩下两个元素的时候,就不需要排序了,而这次相加的费用正是数组中全体元素的和。因此,我们可以在输入时就提前计算全体元素的和,然后循环到i小于n减2即可。

下面给出代码:

#include 
#include 
using namespace std;

int main()
{
    int n;
    while(cin>>n)
    {
        int a[n],sum=0;              //  sum作为总费用
        for(int i=0;i<n;i++)
        {   
            cin>>a[i];          
            sum+=a[i];               //有一组费用我们不难发现,它就是所有元素的和
        }    
        sort(a,a+n);
        for(int i=0;i<n-2;i++)      //因为我们提前加了所有元素的费用,所以只能加到只剩两个元素时 
        {
            int t=a[i]+a[i+1];      //排序后,前两个一定是最小的,我们的删除操作完全可以变成“数组元素的右移”    
            sum+=t;
            a[i+1]=t;               //将最小和赋值给a[i+1],下面再排序
            sort(a+i+1,a+n);
        }
        cout<<sum<<endl;
    }
    return 0;
}

B. 旅行家的预算

这道题是一道贪心+模拟,整体思路如下:
1.枚举途中经过的加油站,每经过一个加油站,计算一次花费
2.在一个加油站所需要加的油,就是能够支持它到达下一个油价比它低的加油站的量;
3.如果在这个加油站即使加满油,都不能到达一个比它油价低的加油站,
(1)就把油箱加满,前往能够到达的加油站中油价最低的那个;
(2)或者直接到终点;
4.如果在这个加油站即使加满油,都不能到达任意一个加油站,也不能到达终点城市,说明无解;

第一点:先考虑2再考虑3,2一定划算,且无后续性
第二点:注意不仅要省钱,还要是最近的加油站(尽早省钱美滋滋)
第四点:前提是不省钱,注意要考虑直接到终点的情形
第五点:为什么要加满油?因为这样可以减少在下一个加油站(价格更贵)所需要加的油量。

下面给出代码:

#include 
#include
#include
using namespace std;
double d1,c,d2,n,p1,lc,ans,yx;
struct station
{
	double d,p;
}a[10005];
int main() 
{
	scanf("%lf%lf%lf%lf%lf",&d1,&c,&d2,&p1,&n);
	lc=c*d2;//总路程 
	for(int i=1;i<=n;i++) scanf("%lf%lf",&a[i].d,&a[i].p);
	double i=0,yq=p1;
	while(i<d1) 
	{
		double t=i+lc,_min=1e9;
		int bh=0;
		for(int j=1;j<=n;j++) 
		{//在范围内寻找能省钱且最近的 
			if(a[j].d>i&&a[j].d<=t&&a[j].d<d1&&a[j].p<yq&&a[j].d<_min) 
			{
			    bh=j,_min=a[j].d;
			} 
		} 
		if(bh!=0) ans+=((a[bh].d-i)*yq-yx)/d2,i=a[bh].d,yq=a[bh].p,yx=0;
		//剩余油不可能直接使到达 
		
		//不能找到,分为能否直接到达终点以及有无后续加油站两方面同时考虑 
		else 
		{
			_min=1e9,bh=0;
			for(int j=1;j<=n;j++) 
			{//在范围内寻找最便宜(并不省钱)的加油站 
			    if(a[j].d>i&&a[j].d<=t&&a[j].d<d1&&a[j].p<_min) _min=a[j].p,bh=j;
		    }
		    //无后续加油站且无法直接到达终点 
		    if(bh==0&&t<d1) 
			{
		        printf("No Solution");
				return 0;
			}
			//能直接到达,最划算 
			if(t>=d1) {
				if(yx<(d1-i)/d2) 
				{
					ans+=((d1-i)/d2-yx)*yq;
				}
				break;
			}
			
			//不能直接到达但有后续加油站,先加满(省钱),记录剩余油的多少
			//加满和到最便宜的地方是关键 
			else 
			{
				ans+=(c-yx)*yq;
			    yx=c-(a[bh].d-i)/d2;
			    yq=a[bh].p,i=a[bh].d;
			}
		}
	}
	printf("%.2lf",ans);
}

C. 完美的代价

解题思路:
1.Impossible
maamdmaam—>奇数个字符出现的次数为1或0时才能实现回文
统计每个字符出现的次数,如果出现奇数的次数>1时,这时表示不能实现回文;
2.从最后面查找第1个出现的位置
3.将一个字符交换到指定的位置
dmama—>mdama
4.奇数个字符的处理

下面给出代码:

#include
#include
#include 
using namespace std;
char arr[8005];
int b[30]; 
int N,count;
int main()
{
	//输入 
	cin>>N;
	for(int i=0;i<N;i++)
	{
		cin>>arr[i]; 
		b[arr[i]-'a']++;
	}
	//统计字符串中每个字符出现的次数 
	//如果字符串中有字符单数的次数>1,表示不可能交换成回文数 
	for(int i=0,flag=0;i<26;i++)
	{
		if(b[i]%2==1)
		{
			if(++flag>1)
			{
			    cout<<"Impossible"<<endl;
				return 0;
			}
		}
	}
	int start=0,j,flag;
    for(int i=0;i<N/2;i++)
	{
	     flag=0;
    	 for(j=N-i-1;j>i;j--)
		 {
    		if(arr[i]==arr[j])
			{
    				for(int start=j;start<N-i-1;start++)
					{
		               char temp=arr[start];
		               arr[start]=arr[start+1];
		               arr[start+1]=temp;
		               count++;
	                }
	                break;//第一次出现相等的时候 
    		}		
        }
	    if(j==i)
		{
    	  char temp=arr[i];
		  arr[i]=arr[i+1];
		  arr[i+1]=temp;
		  count++;
		  flag=1;
    	}
    	if(flag==1)
		{
    		i--;
    	}
    }
	
    cout<<count<<endl;
}

D. 翻硬币

贪心题。
思路是是先对两个字符串进行比较,用数组记录下比较的结果,0表示字符相同,1表示字符不同。
用题目给的例子示范,比较结果为:
1000010000
翻动次数就是两个1之间的下标之差。

当然也有些复杂的情况,若比较结果为: 101101100101
你是直接翻动中间的两个“11”处的硬币再解决其它硬币呢(因为翻动相邻的两个硬币只算一次操作),还是按上面的规则依次计算呢?
这道题“贪心”之处就在这里,事实上从左到右依次按上面蓝字的规则累加计数,就可求得最优结果。

下面给出代码:

#include 
using namespace std;
int main()
{
    char s1[1000];
    char s2[1000];
    int cr[1000];                    //记录两个字符串的比较结果。0为相同,1为不同。 
    while(cin>>s1)
	{
        cin>>s2;
        int l;
        for(l=0;s1[l]!='\0';l++);    //计算长度 
        for(int i=0;i<l;i++)         //比较两个字符串,并记录结果 
		{                            
            if(s1[i]==s2[i])
                cr[i]=0;
            else
                cr[i]=1;
        }
        int f=-1;                   //记录标记位 
        int _count=0;
        for(int i=0;i<l;i++)
		{
            if(cr[i]==1)            //检测到一个 1 
			{           
                if(f==-1)           //如果前面没有记录的1的下标,记录当前1的下标 
				{ 
                    f=i;
                } 
                else         //如果前面有一个1了 
				{    
                    _count+=i-f;
                    f=-1;
                }
            }
        }
        cout<<_count<<endl; 
    }
    return 0;
}

E. 扫雷游戏

此题难度较低,思路如下:
1. 用bool数组a[][]存下地图上的地雷(是地雷为true,不是地雷为false,初始化全为false)
2. 双重循环枚举数组元素,暴力计算结果。

下面给出代码:

#include 
using namespace std;
bool a[105][105];
int n,m;
int main() 
{
    memset(a,0,sizeof(a));
    char tmp;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
	{
        for(int j=1;j<=m;j++) 
		{
            cin>>tmp;
            if(tmp=='*')a[i][j]=1;
        }
    }
    for(int i=1;i<=n;i++)
	{
        for(int j=1;j<=m;j++)
		{
            if(a[i][j]==1)putchar('*');
            else
            printf("%d",a[i+1][j+1]+a[i+1][j-1]+a[i+1][j]+a[i][j+1]+a[i][j-1]+a[i-1][j+1]+a[i-1][j]+a[i-1][j-1]);
        }
        putchar('\n');
    }
    return 0;
}

F. 填涂颜色

这是一道简单的搜索题,把圈外的元素都搜一遍并赋值-1,从一点向四个方向搜索,越界或不等于0时停止;

题意是闭合圈而不是闭合圆,所以不能只设四个角为搜索起点,要把四条边上的所有元素都设置成搜索起点,遇到圈就会return;

现在圈外元素全部为-1,圈内元素为0;圈为1,三级划分已经很明显了,直接按照题意输出就OK;

下面给出代码:

#include 
using namespace std;
int n,a[31][31];
void dfs(int x,int y) 
{
	if (x<1||y<1||x>n||y>n||a[x][y]!=0)return;
	a[x][y]=-1;
	dfs(x+1,y);
	dfs(x-1,y);
	dfs(x,y+1);
	dfs(x,y-1);
}
int main() 
{
	cin>>n;
	for(int i=1;i<=n;i++) 
	{
		for(int j=1;j<=n;j++)
			cin>>a[i][j];
	}
	for(int i=1;i<=n;i++)
	{
		if (a[i][1]!=1) dfs(i,1);
		if (a[i][n]!=1) dfs(i,n);
	}
	for(int i=1;i<=n;i++)
	{
		if (a[1][i]!=1) dfs(1,i);
		if (a[n][i]!=1) dfs(n,i);
	}
	for (int i=1;i<=n;i++) 
	{
		for(int j=1;j<=n;j++) 
		{
			if (a[i][j]==-1) 
			{
				cout<<'0'<<' ';
			}
			else if(a[i][j]==1) 
			{
				cout<<'1'<<' ';
			}
			else 
			{
				cout<<'2'<<' ';
			}
		}
		cout<<endl;
	}
	return 0;
}

G. 轻重搭配

首先想到这题是一道贪心题,我们要让瘦子们和可以和他组的中的最瘦的人组队,但是如果直接让最瘦的与最胖的组,显然不对,因为这样会导致一些不那么胖的人没法组。
考虑到最多组 n/2 对,于是我们可以把所有人分为前后两段,前面一段最瘦的找后一段中满足条件中最瘦的,这样就可以得到正解了

下面给出代码:

#include 
using namespace std;
const int maxn = 5e5+500;
int mp[maxn];
int n,m;
 
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;++i) scanf("%d",&mp[i]);
    sort(mp,mp+n);
    int ans=0;
    for(int i=0,j=1;i<n/2;++i){
        while(mp[i]*2>mp[j]&&j<n) j++;
        if(j>=n) break;
        ans++;
        j++;
    }
    printf("%d\n",n-ans);
    return 0;
}

你可能感兴趣的:(华北理工大学ACM协会2021蓝桥杯第三次训练赛题解)