NEUQ-ACM第四届图灵杯个人赛真题重现题解

用于辅助备战第十届

A.完善新谷密

题目图片都没了,还做什么

B.爬楼梯(*1100)

考查:动态规划

分析:用数组dp[N]表示薯片到达第i个台阶上时,已经丢失了dp[i]个糖果。假设薯片现在正在第0个阶梯上,那么初始状态就是dp[0]=0。如果薯片在第i个阶梯上,用j来表示薯片上一次的步数,那么i一定大于等于j,上一次丢失的糖果就是dp[i-j],加上这一次丢的糖果a[i],现在丢失的糖果就是dp[i-j]+a[i]。因为薯片最多一次上m个阶梯,因此对j循环时从1~m遍历一遍并持续更新dp[i]的值,状态转移方程就是dp[i]=min(dp[i],dp[i-j]+a[i])。

Code:

#include<bits/stdc++.h>
using namespace std;
int a[1010],dp[1010];
int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		for(int i=1;i<=n;i++)  cin>>a[i];
		for(int i=1;i<=n;i++)  dp[i]=1e9;
		dp[0]=0,a[0]=0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				if(i>=j)  dp[i]=min(dp[i],dp[i-j]+a[i]);
		cout<<dp[n]<<'\n';
	}
}

C.吃薯条(*1000)

考查:思维

分析:最优的策略是,我们可以先将所有的薯条分成两半部分,并将右半部分尽可能的吃光,然后对各部分再进行相同的操作(类似于分治?)。假设有6个薯条[1,2,3,4,5,6],我们将它们分成两部分[1,2,3|4,5,6],然后去吃右半部分[1,2,3|0,1,2]。接下来对两部分重复上述操作:

[1,2|3|0,1|2]——>[1,2|1|0,1|0]——>[1|2|1|0|1|0]——>[1|1|1|0|0|0]——>[0|0|0|0|0|0]

答案为f[n]=f[(n+1)/2]+1

Code:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	while(cin>>n)
	{
		int ans=0;
		while(n)
		{
			ans++;
			n/=2;
		}
		cout<<ans<<'\n';
	}
}

D.拿糖果(*1000)

考查:思维

分析:我们对第1~n-1个糖果进行遍历(不可能拿最后一个),因为手上拿的糖果价值已经既定了,因此我们要保证后面拿的糖果价值尽可能小,即要求出i∈[2,n],a[i]~a[n]之间的最小值。这里逆序处理会比较方便,用num[N]记录区间中的最小值,然后倒序遍历。求出num数组后,与代表价值的数组求差然后选出最大值即可。注意最大值初始化时要尽可能小,答案可能为负数。

Code:

#include<bits/stdc++.h>
using namespace std;
int a[100010],num[100010];
int main()
{
	int n;
	while(cin>>n)
	{
		for(int i=1;i<=n;i++)  cin>>a[i];
		num[n]=a[n];
		int ans=a[n-1]-a[n];
		for(int i=n-1;i;i--)
		{
			num[i]=min(a[i],num[i+1]);
			ans=max(ans,a[i]-num[i+1]);
		}
		cout<<ans<<'\n';
	}
}

E.蔡老板的会议(*1700)

考查:最短路算法

分析:说到最短路,那么第一个想到dijkstra算法,但是此算法更适用于单源最短路,即更适合从蔡老板的办公室回去这个子问题。而大家到蔡老板的办公室是多源最短路,更适合用Floyd算法,不过我们依然可以选择dijkstra算法。既然大家一个一个去找蔡老板很麻烦,为什么不反过来让蔡老板去一个一个找他们呢?那么我们就可以进行反向建边,将多源最短路转化成单源最短路,然后进行两次找最短路,将结果相加取最大就是答案(洛谷的图论题集很多都是反向建边,可以康康)。注意本题是多组输入,只是输入描述没写!

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int g[N][N],G[N][N],dist[N],dis[N];
bool st[N];
int n,m,x;
void dijkstra(int u)
{
	memset(dist,0x3f,sizeof(dist));
	memset(st,false,sizeof(st));
    dist[u]=0; 
    for(int i=1;i<=n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dist[t]>dist[j])) t=j;
        st[t]=true;
        for(int j=1;j<=n;j++)
            dist[j]=min(dist[j],dist[t]+g[t][j]);
    }
}
void dijkstra2(int u)
{
	memset(dis,0x3f,sizeof(dis));
	memset(st,false,sizeof(st));
    dis[u]=0;
    for(int i=1;i<=n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
            if(!st[j]&&(t==-1||dis[t]>dis[j])) t=j;
        st[t]=true;
        for(int j=1;j<=n;j++)
            dis[j]=min(dis[j],dis[t]+G[t][j]);
    }
}
int main()
{
    while(cin>>n>>m>>x)
    {
	    memset(g,0x3f,sizeof(g));
	    memset(G,0x3f,sizeof(G));
	    while(m--)
	    {
	        int a,b,c;
	        cin>>a>>b>>c;
	        g[a][b]=min(g[a][b],c);
	        G[b][a]=min(G[b][a],c);
	    }
	    int maxx=0;
	    dijkstra(x);
	    dijkstra2(x);
		for(int i=1;i<=n;i++)  maxx=max(maxx,dist[i]+dis[i]); 
		cout<<maxx<<'\n';
	}
}

F.一个简单的问题(*800)

我想这题没什么好说的

Code: 

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	while(cin>>n)
	{
		for(int i=n-1;i>=0;i--)
		{
			for(int j=1;j<=i;j++)  cout<<' ';
			char c='A';
			for(int j=1;j<=n-i;j++)
			{
				cout<<c;
				c++;
			}
			c--;
			for(int j=1;j<n-i;j++)
			{
				c--;
				cout<<c;
			}
			cout<<'\n';
		}
	}
}

G.粉丝与汉诺塔(*1000)

考查:思维,递推

分析:以普遍情况为例,设A上有n个盘子,则需要f[n]步,转移步骤为:

(1)将A上垫底的盘子上面的n-1个盘子通过B移动到C,即f[n-1]步

(2)将A上剩下的最大一个盘子移动到B,共1步

(3)将C上的n-1个盘子通过B移动到A,共f[n-1]步

(4)将B上的那个最大的盘子移动到C,共1步

(5)将A上的n-1个盘子通过B移动到C,共f[n-1]步

即f[n]=3*f[n-1]+2,n=1时,f[1]=2

Code:

#include<bits/stdc++.h>
using namespace std;
int f[20],n;
int main()
{
	f[1]=2;
	for(int i=2;i<=18;i++)  f[i]=3*f[i-1]+2;
	while(cin>>n)  cout<<f[n]<<'\n';
}

H.粉丝与分割平面(*1500)

考查:思维,递推

结论题

Code:

#include<iostream>
#include<math.h>
using namespace std;
int main()
{
	int t,n,m;
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
		long long x=2*n*n-n+1;
		long long y=2+m*(m-1);
		cout<<x<<" "<<y<<'\n';
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值