蓝桥杯在线测试的题解(二)
蓝桥杯在线测试的题目。不定时不断更新中,没按顺序做。
题目地址: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。
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
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
6
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的边。
1 2 -1
2 3 -1
3 1 2
-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开始标号。
1 2 3 4 5
2
1 5 2
2 3 2
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。
对于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操作的结果。
1 2 3 4
2 1 3
1 4 3
3 1 4
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; }