2018.07.08【2018提高组】模拟C组

本文详细解析了四道编程竞赛题目的解答,包括音乐节拍的时间查找、电视游戏问题的01背包策略、头晕的奶牛的拓扑排序应用、过路费的多源最短路径及最大点权计算。每道题目都附带了代码实现和关键分析思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

今天做得不错吧,……


比赛题目

JZOJ 1619 音乐节拍

题目大意:

n个节拍,对于第i个节拍,持续的时间是Bi−1至Bi−1B_{i-1}至B_i-1Bi1Bi1,q个询问,求在某个时间是第几个节拍。


分析

思路1:(在线)时间复杂度:O(n+qlogn)O(n+qlogn)O(n+qlogn)
由于持续的时间是递增的(前缀和),所以用二分查找找出答案
思路2:(离线)时间复杂度:O(qlogq+q)O(qlogq+q)O(qlogq+q)
把询问的时间从小到大排序,运用前缀和找出答案。


代码(离线)

#include <cstdio>
#include <cctype>
#include <algorithm>
#define us unsigned short
using namespace std;
struct rec{int x; us y;}b[50001]; us n,q,t=1,a[50001],ans[50001]; int tosic=-1;
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
bool cmp(rec x,rec y){return x.x<y.x;}
int main(){
	freopen("mnotes.in","r",stdin);
	freopen("mnotes.out","w",stdout);
	n=in(); q=in();
	for (int i=1;i<=n;i++) a[i]=in();
	for (int i=1;i<=q;i++) b[i]=(rec){in(),i};
	stable_sort(b+1,b+1+q,cmp);
	for (int i=1;i<=n;i++){
		tosic+=a[i];
		while (b[t].x<=tosic&&t<=q) ans[b[t++].y]=i;
	}
	for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
	return 0; 
}

代码(在线)

#include <cstdio>
#include <cctype>
using namespace std;
int n,q,a[50005];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int main(){
	freopen("mnotes.in","r",stdin);
	freopen("mnotes.out","w",stdout);
	n=in(); q=in(); int x;
	for (int i=1;i<=n;i++) x=in(),a[i+1]=a[i]+x;
	while (q--){
		x=in();
	    int l=1,r=n+1;
	    while (l<=r){
		    int mid=(l+r)>>1;
		    if (x>=a[mid]) l=mid+1; else r=mid-1;
	    }
	    printf("%d\n",r);
	}
	return 0; 
}

JZOJ 1620 电视游戏问题

题目大意

n个平台,每个平台上的游戏只能在该平台上运行,游戏和平台都要花费钱,游戏还可以有产出值,求在限定的钱内的最大产出值。


分析

01背包,状态转移方程f[j]=max(f[j],f[j−w[i]]+c[i])f[j]=max(f[j],f[j-w[i]]+c[i])f[j]=max(f[j],f[jw[i]]+c[i])
但是这是对于单个平台的最大产出值,所以再用一个dp[j]表示所有平台的最大产出值,
开始f[j]=dp[j−x](减去平台的费用x)f[j]=dp[j-x](减去平台的费用x)f[j]=dp[jx](x),最后dp[j]=max(dp[j],f[j])(求最大值)dp[j]=max(dp[j],f[j])(求最大值)dp[j]=max(dp[j],f[j])()……


代码

#include <cstdio>
#include <cctype>
using namespace std;
int dp[100001],f[100001],n,m,x,num,w,c;
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int max(int a,int b){return (a>b)?a:b;}
int main(){
	freopen("vidgame.in","r",stdin);
	freopen("vidgame.out","w",stdout);
	n=in(); m=in();
	for (int i=1;i<=n;i++){
		x=in(); num=in();
		for (int j=x;j<=m;j++) f[j]=dp[j-x];//购买平台
		for (int j=1;j<=num;j++){
			w=in(); c=in();
			for (int k=m-w;k>=x;k--)
			f[k+w]=max(f[k+w],f[k]+c);//01背包
		}
		for (int j=x;j<=m;j++) dp[j]=max(dp[j],f[j]);//计算最大值
	}
	return !printf("%d",dp[m]);
}

JZOJ 1621 头晕的奶牛

题目大意:

在一个图中:给出有向边和无向边,且有向边不能改变,把无向边的方向固定,使图变成有向无环图
并输出改变方向的无向边。


分析

可以用拓扑排序,求出点的顺序,当无向边的方向满足顺序则成立
证明:当按拓扑排序后顺序小的连向大的不会有环
那怎么样求拓扑排序,当某点的入点为0加入队列,否则当通过有向边找到一个点将其入度-1重复进行,进入队列的顺序就是答案。
PS:程序里out数组改为in数组


代码

#include <cstdio>
#include <cctype>
#define N 100001
#include <queue>
using namespace std;
int n,m1,m2,x,y1,out[N],y[N],now,next[N],ls[N],top[N]; queue<int>q;
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int main(){
	freopen("dizzy.in","r",stdin);
	freopen("dizzy.out","w",stdout);
	n=in(); m1=in(); m2=in();
	for (int i=1;i<=m1;i++){
		x=in();y[i]=in(); out[y[i]]++;//入点
		next[i]=ls[x]; ls[x]=i;
	}
	for (int i=1;i<=n;i++) if (!out[i]&&ls[i]) q.push(i);//当连接有向边而且没有入点
	while (q.size()){//拓扑排序
		x=q.front();q.pop(); top[x]=++now;
		for (int i=ls[x];i;i=next[i])
		if (out[y[i]]>1) out[y[i]]--; 
		else q.push(y[i]); 
	}
	for (int i=1;i<=m2;i++){
		x=in(); y1=in();
		if (top[x]<top[y1]) printf("%d %d\n",x,y1); else printf("%d %d\n",y1,x);
	}
	return 0;
}

JZOJ 1622 过路费

题目大意:

询问两点间的路费(路径的边权和+路径中最大的点权)


分析

floyd求多源最短路径,对于最大点权,点权排序,最大点权必然是(i,j,k(中间点))的最大值


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
struct rec{int x,y;}c[251]; int d[251];
int n,m,q,cost[251][251],tost[251][251];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
bool cmp(rec a,rec b){return a.x<b.x;}
int min(int a,int b){return (a<b)?a:b;}
int tmax(int a,int b,int c){return (a<b)?((b<c)?c:b):((a<c)?c:a);}
int main(){
	freopen("toll.in","r",stdin);
	freopen("toll.out","w",stdout);
	memset(cost,0x3f,sizeof(cost));
	memset(tost,0x3f,sizeof(tost));
	n=in(); m=in(); q=in();
	for (int i=1;i<=n;i++) c[i]=(rec){in(),i};
	stable_sort(c+1,c+1+n,cmp);//排序
	for (int i=1;i<=n;i++) d[c[i].y]=i;//记录原来的点的当前位置
	while (m--){
		int x=in(); int y=in(); int w=in();
		cost[d[x]][d[y]]=cost[d[y]][d[x]]=min(w,cost[d[y]][d[x]]);//有多条道路
	}
	for (int k=1;k<=n;k++)
	for (int i=1;i<=n;i++)
	for (int j=1;j<=n;j++)
	cost[i][j]=min(cost[i][j],cost[i][k]+cost[k][j]),
	tost[i][j]=min(tost[i][j],cost[i][j]+tmax(c[i].x,c[j].x,c[k].x));//floyd
	while (q--){int x=in(); int y=in(); printf("%d\n",tost[d[x]][d[y]]);}
	return 0;
}

后续

PS:四道题目分别在洛谷的2969 2967 2017 2966

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值