蓝桥杯在线测试的题解(二)

蓝桥杯在线测试的题目。不定时不断更新中,没按顺序做。

题目地址:http://lx.lanqiao.org/index.page

上一次的因为太长,保存好慢,决定在开一篇。

上一篇:蓝桥杯在线测试的题解(一)http://blog.csdn.net/murmured/article/details/18908567(新手推荐,都是水题)


算法训练- 安慰奶牛  

问题描述

Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路。道路被用来连接N个牧场,牧场被连续地编号为1到N。每一个牧场都是一个奶牛的家。FJ计划除去P条道路中尽可能多的道路,但是还要保持牧场之间 的连通性。你首先要决定那些道路是需要保留的N-1条道路。第j条双向道路连接了牧场Sj和Ej(1 <= Sj <= N; 1 <= Ej <= N; Sj != Ej),而且走完它需要Lj的时间。没有两个牧场是被一条以上的道路所连接。奶牛们非常伤心,因为她们的交通系统被削减了。你需要到每一个奶牛的住处去安慰她们。每次你到达第i个牧场的时候(即使你已经到过),你必须花去Ci的时间和奶牛交谈。你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。在早上 起来和晚上回去睡觉的时候,你都需要和在你睡觉的牧场的奶牛交谈一次。这样你才能完成你的 交谈任务。假设Farmer John采纳了你的建议,请计算出使所有奶牛都被安慰的最少时间。

输入格式

第1行包含两个整数N和P。

接下来N行,每行包含一个整数Ci

接下来P行,每行包含三个整数Sj, Ej和Lj

输出格式
输出一个整数, 所需要的总时间(包含和在你所在的牧场的奶牛的两次谈话时间)。
样例输入
5 7
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
样例输出
176
数据规模与约定

5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。



题目的测试样例有误:(P为7怎么才6组。。我把题目的7改为6答案为178)

5 6
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6

思路:

直接把边的权值w(u,v)改为2*w(u,v)+cost[u]+cost[v],然后求MST。why?

纸上画画这组数据(答案43)

3 2
5

7

1 2 3 
2 3 4

然后因为他要找一个地方过夜,所以要多花费一个点的对话,找最小的地方过夜就好了~嘻嘻~

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=10000+10;
const int MAXM=100000+10;
const int INF=100000;
int cost[MAXN],fa[MAXN];
int find(int cur)
{
	return cur==fa[cur]? cur:fa[cur]=find(fa[cur]);
}
struct edge
{
	int from,to,val;
	bool operator <(const edge& x)const{
		return val<x.val;
	}
}e[MAXM];

int main()
{
	int mini=INF,index;
	int n,p;
	scanf("%d%d",&n,&p);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&cost[i]);
		mini=min(cost[i],mini);
		fa[i]=i;
	}
	for(int i=0;i<p;i++)
	{
		scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].val);
		e[i].val=(e[i].val<<1)+cost[ e[i].from ] + cost [ e[i].to ];
	}
	sort(e,e+p);
	int ans=0;
	for(int i=0;i<p;i++)
	{
		int x=e[i].from,y=e[i].to;
		int root_x=find(x),root_y=find(y);
		if(root_x==root_y)   continue;
		fa[root_x]=root_y;
		ans+=e[i].val;
	}
	printf("%d\n",ans+mini);
	return 0;
}



算法训练- 最短路

问题描述

给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。

输入格式

第一行两个整数n, m。

接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。

输出格式
共n-1行,第i行表示1号点到i+1号点的最短路。
样例输入
3 3
1 2 -1
2 3 -1
3 1 2
样例输出
-1
-2
数据规模与约定

对于10%的数据,n = 2,m = 2。

对于30%的数据,n <= 5,m <= 10。

对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。




直接SPFA,直接100分。。

开锦囊说堆优化的dijkstra。。。题目有负边权啊!你确定这不是临时工写的?还是我水平有限?望赐教。。。

#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN=20000+10;
const int MAXM=200000+10;
const int INF=100000;
int head[MAXN],len,dis[MAXN],n,m;
bool vis[MAXN];
struct edge
{
	int to,val,next;
}e[MAXM];

void add(int from,int to,int val)
{
	e[len].to=to;
	e[len].val=val;
	e[len].next=head[from];
	head[from]=len++;
}
queue<int> q;
void spfa()
{
	for(int i=1;i<=n;i++)
		dis[i]=INF;
	memset(vis,0,sizeof(vis));
	q.push(1);
	vis[1]=true;
	dis[1]=0;
	while(!q.empty())
	{
		int cur=q.front();
		q.pop();
		vis[cur]=false;
		for(int i=head[cur];i!=-1;i=e[i].next)
		{
			int id=e[i].to;
			if(dis[id] > dis[cur] + e[i].val)
			{
				dis[id] = dis[cur] + e[i].val;
				if(!vis[id])
				{
					vis[id]=true;
					q.push(id);
				}
			}
		}
	}
}
int main()
{
	memset(head,-1,sizeof(head));
	len=0;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int from,to,val;
		scanf("%d%d%d",&from,&to,&val);
		add(from,to,val);
	}
	spfa();
	for(int i=2;i<=n;i++)
		printf("%d\n",dis[i]);

	return 0;
}


  算法训练- 区间k大数查询  

问题描述

给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。

输入格式

第一行包含一个数n,表示序列长度。

第二行包含n个正整数,表示给定的序列。

第三个包含一个正整数m,表示询问个数。

接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。

输出格式
总共输出m行,每行一个数,表示询问的答案。
样例输入
5
1 2 3 4 5
2
1 5 2
2 3 2
样例输出
4
2
数据规模与约定

对于30%的数据,n,m<=100;

对于100%的数据,n,m<=1000;

保证k<=(r-l+1),序列中的数<=106


初看题目,划分树!吓尿了,就跟着模版写过,不会自己写。。。一看规模。。。1000.。我还是排序吧。。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1000+10;
int a[MAXN];
bool cmp(int x,int y)
{
	return x>y;
}
int main()
{
	int n,m;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	scanf("%d",&m);
	while(m--)
	{
		int temp[MAXN],len=0;
		int from,to,k;
		scanf("%d%d%d",&from,&to,&k);
		for(int i=from;i<=to;i++)
			temp[len++]=a[i];
		sort(temp,temp+len,cmp);
		printf("%d\n",temp[k-1]);
	}
	return 0;
}


算法训练- K好数

问题描述

如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数。求L位K进制数中K好数的数目。例如K = 4,L = 2的时候,所有K好数为11、13、20、22、30、31、33 共7个。由于这个数目很大,请你输出它对1000000007取模后的值。

输入格式

输入包含两个正整数,K和L。

输出格式
输出一个整数,表示答案对1000000007取模后的值。
样例输入
4 2
样例输出
7
数据规模与约定

对于30%的数据,KL <= 106

对于50%的数据,K <= 16, L <= 10;

对于100%的数据,1 <= K,L <= 100。



#include<cstdio>
const int MAXN=100+10;
const int mod=1000000007;
__int64 dp[MAXN][MAXN];
int main()
{
	//设dp[i][j]为以j结尾的长度为i的k好数的数目
	int k,L;
	scanf("%d%d",&k,&L);
	for(int i=1;i<k;i++)
		dp[1][i]=1;

	for(int i=2;i<=L;i++)
		for(int j=0;j<k;j++)
			for(int x=0;x<k;x++)
				if(x!=j-1 && x!=j+1)
					dp[i][j]=(dp[i][j]+dp[i-1][x])%mod;
	
	__int64 ans=0;
	//最后把长度为L的0~k-1的k好数加起来
	for(int i=0;i<k;i++)
		ans=(ans+dp[L][i])%mod;
	printf("%I64d\n",ans);
	return 0;
}

算法训练- 操作格子

问题描述

有n个格子,从左到右放成一排,编号为1-n。

共有m次操作,有3种操作类型:

1.修改一个格子的权值,

2.求连续一段格子权值和,

3.求连续一段格子的最大值。

对于每个2、3操作输出你所求出的结果。

输入格式

第一行2个整数n,m。

接下来一行n个整数表示n个格子的初始权值。

接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。

输出格式

有若干行,行数等于p=2或3的操作总数。

每行1个整数,对应了每个p=2或3操作的结果。

样例输入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
样例输出
6
3
数据规模与约定

对于20%的数据n <= 100,m <= 200。

对于50%的数据n <= 5000,m <= 5000。

对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。

线段树水水版~


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100000+10;
int a[MAXN],maxv[MAXN<<2],sum[MAXN<<2];
void build(int k,int L,int R)
{
	if(L==R) {
		sum[k]=maxv[k]=a[L];
	}
	else
	{
		int m=(L+R)>>1;
		build(k<<1,L,m);
		build((k<<1)+1,m+1,R);
		sum[k]=sum[k<<1]+sum[(k<<1)+1];
		maxv[k]=max(maxv[k<<1],maxv[(k<<1)+1]);
	}
}

void update(int k,int L,int R,int p,int v)
{
	if(L==R){
		sum[k]=maxv[k]=v;
	}
	else
	{
		int m=(L+R)>>1;
		if(p<=m) update(k<<1,L,m,p,v);
		if(m<p)	update((k<<1)+1,m+1,R,p,v);
		sum[k]=sum[k<<1]+sum[(k<<1)+1];
		maxv[k]=max(maxv[k<<1],maxv[(k<<1)+1]);
	}
}

int getsum(int k,int L,int R,int a,int b)
{
	if(a<=L && R<=b)
		return sum[k];
	else
	{
		int res=0,m=(L+R)>>1;
		if(a<=m) res+=getsum(k<<1,L,m,a,b);
		if(m<b) res+=getsum((k<<1)+1,m+1,R,a,b);
		return res;
	}
}
int getmax(int k,int L,int R,int a,int b)
{
	if(a<=L && R<=b)
		return maxv[k];
	else
	{
		int res=-0x7ffffff,m=(L+R)>>1;
		if(a<=m) res=max(res,getmax(k<<1,L,m,a,b));
		if(m<b) res=max(res,getmax((k<<1)+1,m+1,R,a,b));
		return res;
	}
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	build(1,1,n);
	
	for(int i=0;i<m;i++)
	{
		int cmd,a,b;
		scanf("%d%d%d",&cmd,&a,&b);
		if(cmd==1)
		{
			update(1,1,n,a,b);
		}
		else if(cmd==2)
		{
			printf("%d\n",getsum(1,1,n,a,b));
		}
		else
		{
			printf("%d\n",getmax(1,1,n,a,b));
		}
	}
	return 0;
}


posted @ 2014-02-16 19:25  hr_whisper  阅读(318)  评论(0编辑  收藏  举报