CCF-CSP:2014-12(前4题)

第一题:

门禁系统


问题描述

涛涛最近要负责图书馆的管理工作,需要记录下每天读者的到访情况。每位读者有一个编号,每条记录用读者的编号来表示。给出读者的来访记录,请问每一条记录中的读者是第几次出现。

输入格式

  输入的第一行包含一个整数n,表示涛涛的记录条数。
  第二行包含n个整数,依次表示涛涛的记录中每位读者的编号。

输出格式

  输出一行,包含n个整数,由空格分隔,依次表示每条记录中的读者编号是第几次出现。

样例输入

5
1 2 1 1 3

样例输出

1 1 2 3 1

评测用例规模与约定

  1≤n≤1,000,读者的编号为不超过n的正整数。


解决方案:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
	int n;
	cin>>n;
	int a[1010]={0};
	for(int i=0;i<n;i++){
		int k;
		cin>>k;
		a[k]++;
		cout<<a[k]<<" ";
	}
}

第二题:

Z字形扫描


问题描述

在图像编码的算法中,需要将一个给定的方形矩阵进行Z字形扫描(Zigzag Scan)。给定一个n×n的矩阵,Z字形扫描的过程如下图所示:


  对于下面的4×4的矩阵,
  1 5 3 9
  3 7 5 6
  9 4 6 4
  7 3 1 3
  对其进行Z字形扫描后得到长度为16的序列:
  1 5 3 9 7 3 9 5 4 7 3 6 6 4 1 3
  请实现一个Z字形扫描的程序,给定一个n×n的矩阵,输出对这个矩阵进行Z字形扫描的结果。

输入格式

  输入的第一行包含一个整数n,表示矩阵的大小。
  输入的第二行到第n+1行每行包含n个正整数,由空格分隔,表示给定的矩阵。

输出格式

  输出一行,包含n×n个整数,由空格分隔,表示输入的矩阵经过Z字形扫描后的结果。

样例输入

4
1 5 3 9
3 7 5 6
9 4 6 4
7 3 1 3

样例输出

1 5 3 9 7 3 9 5 4 7 3 6 6 4 1 3

评测用例规模与约定

  1≤n≤500,矩阵元素为不超过1000的正整数。


解决方案:

找到每条连线的规律,每条线横纵坐标相加的值相等,且为递增或递减

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
	int n;
	int a[510][510];
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&a[i][j]);
		}
	}
	for(int k=2;k<=2*n;k++){
		if(k%2==0){
			for(int i=min(n,k-1);i>=1;i--){
				for(int j=1;j<=min(n,k-1);j++){
					if(i+j==k){
						cout<<a[i][j]<<" ";
					}
				}
			}
		}
		else if(k%2==1){
			for(int i=1;i<=min(n,k-1);i++){
				for(int j=min(n,k-1);j>=1;j--){
					if(i+j==k){
						cout<<a[i][j]<<" ";
					}
				}
			}
		}
	}
}

第三题:

集合竞价


问题描述

某股票交易所请你编写一个程序,根据开盘前客户提交的订单来确定某特定股票的开盘价和开盘成交量。
  该程序的输入由很多行构成,每一行为一条记录,记录可能有以下几种:
  1. buy p s 表示一个购买股票的买单,每手出价为p,购买股数为s。
  2. sell p s 表示一个出售股票的卖单,每手出价为p,出售股数为s。
  3. cancel i表示撤销第i行的记录。
  如果开盘价为p0,则系统可以将所有出价至少为p0的买单和所有出价至多为p0的卖单进行匹配。因此,此时的开盘成交量为出价至少为p0的买单的总股数和所有出价至多为p0的卖单的总股数之间的较小值。
  你的程序需要确定一个开盘价,使得开盘成交量尽可能地大。如果有多个符合条件的开盘价,你的程序应当输出最高的那一个。

输入格式

  输入数据有任意多行,每一行是一条记录。保证输入合法。股数为不超过108的正整数,出价为精确到恰好小数点后两位的正实数,且不超过10000.00。

输出格式

  你需要输出一行,包含两个数,以一个空格分隔。第一个数是开盘价,第二个是此开盘价下的成交量。开盘价需要精确到小数点后恰好两位。

样例输入

buy 9.25 100
buy 8.88 175
sell 9.00 1000
buy 9.00 400
sell 8.92 400
cancel 1
buy 100.00 50

样例输出

9.00 450

评测用例规模与约定

  对于100%的数据,输入的行数不超过5000。


解决方案:

撤销第i行记录,不能直接把cancle覆盖,会导致撤销的行数发生错误。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
struct{
	string str;
	double p;
	int s;
}a[5010];

int main()
{
	int i=0;
	
	while(cin>>a[i].str)
	{
		if(a[i].str=="cancel"){
			scanf("%d",&a[i].s);
			a[a[i].s-1].str.clear();//把撤销那行字符串清空
			i++;
		//	cout<<a[a[i].s].str.size();
			}
		else{
			scanf("%lf%d",&a[i].p,&a[i].s);
			i++;
		}
		
	}
	long long  s0=0,sb=0,ss=0,s1=0;
	double p0=0,pp=0;
	for(int m=0;m<i;m++){
	    sb=0;
	    ss=0;
	    if(a[m].str.size()!=0){
	        pp=a[m].p;//价格一定是记录中的价格,所以对所有价格暴力枚举
		   // cout<<pp<<endl;
	    }
	    else{
	        continue;//若已经撤销,此条记录的价格跳过
	    }
		for(int n=0;n<i;n++){//遍历所有未撤销的记录
			if(a[n].str=="buy"&&a[n].p>=pp){
				sb+=a[n].s;
			}
			if(a[n].str=="sell"&&a[n].p<=pp){
				ss+=a[n].s;
			}
			
		}
		s1=min(ss,sb);//取二者中较小值
		if(s0<s1||(s0==s1&&p0<pp)){
			s0=s1;
			p0=pp;
		}
	}
	printf("%.2lf %lld\n",p0,s0);
	
}

第四题:

最优灌溉


问题描述

雷雷承包了很多片麦田,为了灌溉这些麦田,雷雷在第一个麦田挖了一口很深的水井,所有的麦田都从这口井来引水灌溉。
  为了灌溉,雷雷需要建立一些水渠,以连接水井和麦田,雷雷也可以利用部分麦田作为“中转站”,利用水渠连接不同的麦田,这样只要一片麦田能被灌溉,则与其连接的麦田也能被灌溉。
  现在雷雷知道哪些麦田之间可以建设水渠和建设每个水渠所需要的费用(注意不是所有麦田之间都可以建立水渠)。请问灌溉所有麦田最少需要多少费用来修建水渠。

输入格式

  输入的第一行包含两个正整数n, m,分别表示麦田的片数和雷雷可以建立的水渠的数量。麦田使用1, 2, 3, ……依次标号。
  接下来m行,每行包含三个整数ai, bi, ci,表示第ai片麦田与第bi片麦田之间可以建立一条水渠,所需要的费用为ci。

输出格式

  输出一行,包含一个整数,表示灌溉所有麦田所需要的最小费用。

样例输入

4 4
1 2 1
2 3 4
2 4 2
3 4 3

样例输出

6

样例说明

  建立以下三条水渠:麦田1与麦田2、麦田2与麦田4、麦田4与麦田3。

评测用例规模与约定

  前20%的评测用例满足:n≤5。
  前40%的评测用例满足:n≤20。
  前60%的评测用例满足:n≤100。
  所有评测用例都满足:1≤n≤1000,1≤m≤100,000,1≤ci≤10,000。


解决方案:

//Kruskal算法

#include<iostream>
#include<algorithm>

using namespace std;
const int N=100010;
int n,m;//n为点数,m为边数
int p[N];//并查集的父节点数组 
struct Edge{
	int a,b,w;
	bool operator<(const Edge &W)const{
		return w<W.w;
	}
}edges[N];

int find(int x){
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		p[i]=i;
	}
	for(int i=0;i<m;i++){
		int a,b,w;
		cin>>a>>b>>w;
		edges[i]={a,b,w};
	}
	sort(edges,edges+m);
	int res=0;
	for(int i=0;i<m;i++){
		int a=edges[i].a,b=edges[i].b,w=edges[i].w;
		if(find(a)!=find(b)){
			res+=w;
			p[find(a)]=find(b);
		}
	}
	 cout<<res<<endl;
	 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值