“浪潮杯”第九届山东省ACM大学生程序设计竞赛重现赛 题解

版权声明:低调地前行,越努力越幸运! https://blog.csdn.net/SSYITwin/article/details/83036674

点击转到(牛客网)

A. Anagram

1.思路:

      给定两个字符串,假设是ELLY与KRIS,E到K是6,L到R是6,当第二个L到I时,L是比I大的,此时L就要绕到Z,从Z到A,再从A开始到I,这样长度就是23,Y到S同理,长度是20;这样找完之后序列长度之和就是6 +6+23+20=55.这是题目中给出的一种解答。但是题目要求我们找字符间最小的长度,我就把第一个字符串中的每一个字符与第二个字符串中的每一个字符比较,每次都找出最短的长度,然后加在一起即可。

   举例:ELLY与KRIS,第一个字符串中的第一个字符E与第二个字符串中的每一个字符比较,找出最少的长度并记录下来;再从第一个字符串中的第二个字符L与第二个字符串中的每一个字符比较,依旧找最小的,就这样依次循环,把长度累加即可。

2.代码:

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<30;
char str1[55],str2[55];
char s1[55],s2[55];
int vis[100];
int main()
{
	while(scanf("%s%s",str1,str2)!=EOF)
	{
		memset(vis,0,sizeof(vis));
		int sum=0;
		int len1=strlen(str1);
		int len2=strlen(str2);
		for(int i=0;i<len1;i++)
		  s1[i]=str1[i];
		for(int i=0;i<len2;i++)
		  s2[i]=str2[i];
		for(int i=0;i<len1;i++)
		{
			int minn=maxn;
			int temp;
			for(int j=0;j<len2;j++)
			{
				if(vis[j]==0)
				{
					if((s1[i]-'0')-(s2[j]-'0')>0)
					{
					   if(abs((s1[i]-'0')-(s2[j]-'0')-26)<minn)
					   {
					    	minn=abs((s1[i]-'0')-(s2[j]-'0')-26);
						    temp=j;				   	
					   }
					}
					else
					{
					 if(abs((s1[i]-'0')-(s2[j]-'0'))<minn)
					   {
					    	minn=abs((s1[i]-'0')-(s2[j]-'0'));
						    temp=j;				   	
					   }
					}
				}
			}
			vis[temp]=1;
			sum=sum+minn;
		}
		printf("%d\n",sum);
	}
	return 0;
}

B.Bullet

1.题目含义:

   在GGO,一个以枪支和钢铁为主导的世界,球员们正在争取成为最强大的枪手。玩家Shino是一个狙击手,她的目标射击一次杀死一个怪物。现在她在n * n地图中,并且在某些网格中有怪物。每个怪物都有经验。然而,作为一个大师,诗乃有一种奇怪的自我约束。她最多会杀死一列中的一个怪物,最多也会杀死一行中的一个怪物。现在,她希望知道如何在杀死尽可能多的怪物的前提下获得最大的体验。

2.思路:

    明显来看,是二分,求最小最大问题,把消灭的怪兽的数量作为一个衡量的标准,对此二分图求出最大匹配ans。那么ans为最多能消灭的怪物数量。然后二分枚举经验值k,对于所有矩阵内值w[i][j]>=k的,按照上面一样重新建图并求出最大匹配值。如果求出的值等于ans,那么经验值k是合法的。即求出了最大经验值。

3.代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=550;
int n;
int from[maxn],w[maxn][maxn],ans,vis[maxn];
vector<int> g[maxn];
bool Find(int u)
{
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(!vis[v])
        {
            vis[v]=1;
            if(from[v]==-1 || Find(from[v]))
            {
                from[v]=u;
                return true;
            }
        }
    }
    return false;
}
int match()//求出最大能射死怪兽的数量
{
    int ret=0;
    memset(from,-1,sizeof(from));
    for(int i=0;i<n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(Find(i)  ) ret++;
    }
    return ret;
}
bool check(int k)
{
    for(int i=0;i<n;i++)
        g[i].clear();
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        if(w[i][j]>=k)
            g[i].push_back(j);
    return match()==ans;
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
            g[i].clear();
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            scanf("%d",&w[i][j]);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
        {
            if(w[i][j])
            {
                g[i].push_back(j);
            }
        }
        ans=match();
        int L=0,R=1e9;
        while(L<R)
        {
            int mid=(L+R+1)>>1;
            if(check(mid))
                L=mid;
            else
                R=mid-1;
        }
        printf("%d\n",L);
    }
    return 0;
}

 C.Cities

1.题意:

  Byteland有n个城市,i城市有价值a。 在两个城市之间建立双向道路的成本是它们的价值的总和。 请计算连接这些城市的最低成本,这意味着任何两个城市都可以相互联系.

2.思路:

    贪心算法:想要将任意两个城市连接且道路花费之和最小,那可使每条道路的花费最少,道路的花费等于两端城市value之和,由此可知,只要每个城市与最小value的那个城市相连通,所得的花费必定是最小的。

因此,将最小value的城市放于中间与其他城市一一相连。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define p 100001
using namespace std;
int main(){
    int t;
    int a[p];
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
         if(n==1){
            printf("0\n");
            continue;
        }
        sort(a,a+n);
        int minn=a[0];
        long long sum=0;
        for(int i=1;i<n;i++){
            sum=sum+a[i]+minn;
        }
        printf("%lld\n",sum);
    }
}

D.Dance

1.题意:

    一个树形结构(根节点是0),给定每个节点的父节点的编号,手轴(hand scroll)个数,从该节点到父节点完成一次升级释放的能量。

   升级规则如下:必须从最低层开始,逐层升级,从底层到上一级需消耗一个手轴(hand scroll)才能完成升级,直到升级到最高层,这个时候升级的总能量。

2.

 

其实,题意明确了之后,很容易看到满足要求的路径总共有三条(从叶节点到根),通过这三条路径完成一次升级所释放的能量也是固定的,到底先让哪条先完成升级呢?

显然,每条路径到底能够升级多少次,受手轴(hand scroll)个数的制约。要想让能量最高,那就先让能量高的路径优先完成升级。

3.代码:

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int n,fa[maxn],pre[maxn],num[maxn],power[maxn];
vector<int> son[maxn];
int value[maxn],leaf[maxn],cnt;

/*
dfs()时间复杂度分析:
	对于具有n个顶点、e条边的图来说,dfs算法对图中的每个顶点最多调用一次,因此其递归调用总次数为n。当访问某个
顶点v时,dfs的时间主要花在从该顶点出发查找它的邻接点上。当用邻接表表示图时,需要遍历该顶点的所有邻接点,所有dfs
的总时间为O(n+e);当用邻接矩阵表示图时,需要遍历该顶点行的所有元素,所以dfs的总时间为O(n^2). 
*/

//对于树来说,e=n-1。用邻接表存储树,所以,dfs时间复杂度为O(n) 

void dfs(int id,int w){   //找到叶节点以及每条路径完成每次升级获得到的能量 
	value[id]=w;
	if(son[id].size()==0){
		leaf[cnt++]=id;
		return ;
	}
	int len=son[id].size();
	for(int i=0;i<len;i++){
		int x=son[id][i];
		//printf("power[%d]=%d\n",x,power[x]);
		dfs(x,w+power[x]);
	}
}

bool cmp(int a,int b){
	return value[a]>value[b];
}

int previs(int x,int w){
    if(x==0) return w;
    if(num[x]==0)	return 0;
    if(num[x]<w){w=num[x];num[x]=0;}
    else num[x]-=w;
    return previs(fa[x],w);
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&fa[i],&num[i],&power[i]);
		son[fa[i]].push_back(i);
	}
	
	dfs(0,0); 
	
	/*for(int i=0;i<cnt;i++){
		printf("%d %d\n",leaf[i],value[leaf[i]]);
	}*/
	sort(leaf,leaf+cnt,cmp);
	
	ll ans=0;
	int min_num;
	for(int i=0;i<cnt;i++){  //cnt条路径,由路径value[]从大到小进行求能量值 
		int x=leaf[i];
		min_num=previs(x,num[x]); //注意:这里求每段路径的最小手轴数用递归来求,递推的话会超时
		ans+=1ll*min_num*value[x];
	}
	
	/*
	//超时 
	for(int i=0;i<cnt;i++){  //cnt条路径,由路径value[]从大到小进行求能量值 
		int x=leaf[i];
		min_num=num[x];
		int y=fa[x]; 
		while(y){
			min_num=min(min_num,num[y]);
			y=fa[y];
		}
		ans+=1ll*min_num*value[x];
		
		if(min_num){
			y=x;
			while(y){
				num[y]-=min_num;
				y=fa[y];
			}
		}
	}
	
	*/
	
	printf("%lld\n",ans);
	return 0;
}

 

E Sequence

1.题目含义:

   存在j< ia[j]< a[i],首先存在一个good序列,让我们从1n个元素中,移除一个元素,使得good数最大化。good数的定义就是题目中的定义:j< ia[j]< a[i],只要满足这样一个条件,就存在good数。为了判断删除序列中的哪一个元素使得good数最大化,就要对每一个数字做判断,用cnt[x]函数代表,删除x后,会减少的good数,找出使得减少的good数最少的元素,删除即可。如果存在多个相同对的good数,就删除最小的那个good数。

2.代码:

  

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000005;
int cnt[maxn];
int main()
{
	int t;
	while(scanf("%d",&t)!=EOF)
	{
	  while(t--)
	  {
	     int n;
		 scanf("%d",&n);
		 int min1=maxn,min2=maxn;
		for(int i=0;i<n;i++)
		{
			int x;
			scanf("%d",&x);
			cnt[x]=0;
			if(min1<x && x<min2)
			{
//此时min1<x<min2, min1的存在使得x成为good数,删除min1,x就不是good数
//所以删除min1之后,good数会减1 
				cnt[min1]++;
			}
			if(min1<x)
			{
//min1<x,x本身就是一个good数,删除x本身之后,good数就会减1 
				cnt[x]++;
			}
			if(x<min1)
			{
//此时更新min1与min2的值,使得min1与min2的值为x元素之前的最小值与次最小值 
				min2=min1;
				min1=x;
			}
			else
			{
				if(x<min2)
				{
//更新min2的值,使得min2的更新为x元素之前的次最小值 
					min2=x;
				}
			}
		}
		 int mini=maxn,ans;
		 for(int i=1;i<=n;i++)
		 {
		 	if(cnt[i]==mini)//如果有多个good数,那么就选最小的那一个 
		 	{
		 	   ans=min(ans,i);
			}
		 	else if(cnt[i]<mini)
		 	   mini=cnt[i],ans=i;//找使得good数减少的最少的数,这个数就是要移除对的数 
		 }
		 printf("%d\n",ans);
	  }	
	}
	return 0;
}

F Four-tuples

1.题意:

   给四个区间,要求每个区间选一个数字组成一个四元组(x1,x2,x3,x4),要求是x1!=x2,x2!=x3,x3!=x4,x4!=x1。

每一个区间的长度代表区间中数字的个数,从每一个区间中取出一个数字组成一个四元组,就像数学中的组合一样,组合的总数就是四个区间长度的乘积,但是题目中明确了不合法的条件,所以用总数再减去x1==x2,x2==x3,x3==x4,x4==x1这四种两两相交的情况,再加回所有三个集合相交的部分,再减去所有四个集合相交的部分。

2.题解:

  用到四个集合的容斥定理:

 |A∪B∪C∪D|=|A|+|B|+|C|+|D|-|A∩B|-|A∩C|-|A∩D|- |B∩C| - |B∩D| - |C∩D|+|A∩B∩C|+|A∩B∩D|+|A∩C∩D|+|B∩C∩D| -|A∩B∩C∩D|

3.代码:

  

#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
ll mod=1e9+7;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		ll l1,r1,l2,r2,l3,r3,l4,r4;
		ll left, right,accl,accr;
		scanf("%lld%lld%lld%lld%lld%lld%lld%lld",&l1,&r1,&l2,&r2,&l3,&r3,&l4,&r4);
		ll ans=(r1-l1+1)%mod*(r2-l2+1)%mod*(r3-l3+1)%mod*(r4-l4+1)%mod;
		//x1==x2
		left=max(l1,l2);
		right=min(r1,r2);
		if(left<=right)
		  ans=((ans-(right-left+1)*(r3-l3+1)%mod*(r4-l4+1)%mod)%mod+mod)%mod;
		  
		//x2==x3
		left=max(l2,l3);
		right=min(r2,r3);
		if(left<=right)
		  ans=((ans-(r1-l1+1)*(right-left+1)%mod*(r4-l4+1)%mod)%mod+mod)%mod;
		  
		//x3==x4
		left=max(l3,l4);
		right=min(r3,r4);
		if(left<=right)
		  ans=((ans-(r1-l1+1)*(r2-l2+1)%mod*(right-left+1)%mod)%mod+mod)%mod;
		  
		//x4==x1
		left=max(l1,l4);
		right=min(r1,r4);
		if(left<=right)
		  ans=((ans-(right-left+1)*(r2-l2+1)%mod*(r3-l3+1)%mod)%mod+mod)%mod;
		
		//x1==x2 && x2==x3
		left=max(l1,max(l2,l3));
		right=min(r1,min(r2,r3));
		if(left<=right)
		  ans=(ans+(right-left+1)*(r4-l4+1)%mod)%mod;
		
		//x1==x2 && x2==x4
		left=max(l1,max(l2,l4));
		right=min(r1,min(r2,r4));
		if(left<=right)
	      ans=(ans+(right-left+1)*(r3-l3+1)%mod)%mod;
	      
	    //x1==x2 && x3==x4
	    left=max(l1,l2);
	    right=min(r1,r2);
	    accl=max(l3,l4);
	    accr=min(r3,r4);
	    if(left<=right && accl<=accr)
	      ans=(ans+(right-left+1)*(accr-accl+1)%mod)%mod;
	      
	    //x2==x3 && x3==x4
	    left=max(l2,max(l3,l4));
	    right=min(r2,min(r3,r4));
	    if(left<=right)
	      ans=(ans+(r1-l1+1)*(right-left+1)%mod)%mod;
	      
	    //x2==x3 && x1==x4
	    left=max(l2,l3);
	    right=min(r2,r3);
	    accl=max(l1,l4);
	    accr=min(r1,r4);
	    if(left<=right && accl<=accr)
	      ans=(ans+(right-left+1)*(accr-accl+1)%mod)%mod;
	    
	    //x3==x4 && x1==x4
	    left=max(l1,max(l3,l4));
	    right=min(r1,min(r3,r4));
	    if(left<=right)
	      ans=(ans+(right-left+1)*(r2-l2+1)%mod)%mod;
	      
	    //x1==x2 && x2==x3 && x3==x4
	    left=max(max(l1,l2),max(l3,l4));
	    right=min(min(r1,r2),min(r3,r4));
	    if(left<=right)
	      ans=((ans-(right-left+1)*3)%mod+mod)%mod; //注意减去的是3倍的x1==x2 && x2==x3 && x3==x4。
	      
	    printf("%lld\n",ans);
	}
	return 0;
}

G Games(点击转到)

H Dominoes(点击转到)

特别感谢各位小可爱们,大家一起整理,真好。呀呀呀(斜眼笑)!!!

猜你喜欢

转载自blog.csdn.net/SSYITwin/article/details/83036674