CCF CSP 认证 2014-03

T1

问题描述

试题编号:201403-1
试题名称:相反数
时间限制:1.0s
内存限制:256.0MB
问题描述:

问题描述

  有 N 个非零且各不相同的整数。请你编一个程序求出它们中有多少对相反数(a 和 -a 为一对相反数)。

输入格式

  第一行包含一个正整数 N。(1 ≤ N ≤ 500)。
  第二行为 N 个用单个空格隔开的非零整数,每个数的绝对值不超过1000,保证这些整数各不相同。

输出格式

  只输出一个整数,即这 N 个数中包含多少对相反数。

样例输入

5
1 2 3 -1 -2

样例输出

2

思路:用标记数组Arr[r]记录出现次数,初始的时候Arr[r]=0;遍历整数,记当前遍历到的值r>0时,此时观察Arr[r]的值,若Arr[r]<0,表示之前有-Arr[r]个未被匹配的-r,可以和r匹配,而Arr[r]+1,表示-r匹配一个,答案累计加1,r<0时同理。

#include<iostream>
using namespace std;

int Arr[1010];

int main(){
	int i,n,r,ans=0;
	cin>>n;
	for(i=1;i<=n;++i){
		cin>>r;
		if(r>=0){
			if(Arr[r])++ans;
			++Arr[r];
		}
		else{
			if(Arr[-r])++ans;
			--Arr[-r];
		}
	}
	cout<<ans;
	return 0;
}

T2

问题描述

试题编号:201403-2
试题名称:窗口
时间限制:1.0s
内存限制:256.0MB
问题描述:

问题描述

  在某图形操作系统中,有 N 个窗口,每个窗口都是一个两边与坐标轴分别平行的矩形区域。窗口的边界上的点也属于该窗口。窗口之间有层次的区别,在多于一个窗口重叠的区域里,只会显示位于顶层的窗口里的内容。
  当你点击屏幕上一个点的时候,你就选择了处于被点击位置的最顶层窗口,并且这个窗口就会被移到所有窗口的最顶层,而剩余的窗口的层次顺序不变。如果你点击的位置不属于任何窗口,则系统会忽略你这次点击。
  现在我们希望你写一个程序模拟点击窗口的过程。

输入格式

  输入的第一行有两个正整数,即 N 和 M。(1 ≤ N ≤ 10,1 ≤ M ≤ 10)
  接下来 N 行按照从最下层到最顶层的顺序给出 N 个窗口的位置。 每行包含四个非负整数 x1, y1, x2, y2,表示该窗口的一对顶点坐标分别为 (x1, y1) 和 (x2, y2)。保证 x1 < x2,y1 2。
  接下来 M 行每行包含两个非负整数 x, y,表示一次鼠标点击的坐标。
  题目中涉及到的所有点和矩形的顶点的 x, y 坐标分别不超过 2559 和  1439。

输出格式

  输出包括 M 行,每一行表示一次鼠标点击的结果。如果该次鼠标点击选择了一个窗口,则输出这个窗口的编号(窗口按照输入中的顺序从 1 编号到 N);如果没有,则输出"IGNORED"(不含双引号)。

样例输入

3 4
0 0 4 4
1 1 5 5
2 2 6 6
1 1
0 0
4 4
0 5

样例输出

2
1
1
IGNORED

样例说明

  第一次点击的位置同时属于第 1 和第 2 个窗口,但是由于第 2 个窗口在上面,它被选择并且被置于顶层。
  第二次点击的位置只属于第 1 个窗口,因此该次点击选择了此窗口并将其置于顶层。现在的三个窗口的层次关系与初始状态恰好相反了。
  第三次点击的位置同时属于三个窗口的范围,但是由于现在第 1 个窗口处于顶层,它被选择。
  最后点击的 (0, 5) 不属于任何窗口。

思路:直接模拟,需要的注意的是窗口的顺序以及点击后需要改变部分窗口的位置。

#include<iostream>
using namespace std;

const int N=15,M=15;

struct Window{
	int Lx,Ly,Rx,Ry,index;
	bool have(int x,int y){
		return Lx<=x&&Rx>=x&&Ly<=y&&Ry>=y;
	}
};

Window windows[N];

//越靠后越顶层 
int main(){
	int n,m,i,j,x,y;
	Window temp;
	cin>>n>>m;
	for(i=1;i<=n;++i){
		cin>>windows[i].Lx>>windows[i].Ly>>windows[i].Rx>>windows[i].Ry;
		windows[i].index=i;
	}
	for(i=1;i<=m;++i){
		cin>>x>>y;
		for(j=n;j>0;--j){
			if(windows[j].have(x,y)){//顶层窗口 
				break;
			}
		}
		if(j==0)cout<<"IGNORED"<<endl;
		else{
			cout<<windows[j].index<<endl;
			temp=windows[j];
			for(;j<n;++j){
				windows[j]=windows[j+1];
			}
			windows[n]=temp;
		}
	}
	return 0;
}

T3

问题描述

试题编号:201403-3
试题名称:命令行选项
时间限制:1.0s
内存限制:256.0MB
问题描述:

问题描述

  请你写一个命令行分析程序,用以分析给定的命令行里包含哪些选项。每个命令行由若干个字符串组成,它们之间恰好由一个空格分隔。这些字符串中的第一个为该命令行工具的名字,由小写字母组成,你的程序不用对它进行处理。在工具名字之后可能会包含若干选项,然后可能会包含一 些不是选项的参数。
  选项有两类:带参数的选项和不带参数的选项。一个合法的无参数选项的形式是一个减号后面跟单个小写字母,如"-a" 或"-b"。而带参数选项则由两个由空格分隔的字符串构成,前者的格式要求与无参数选项相同,后者则是该选项的参数,是由小写字母,数字和减号组成的非空字符串。
  该命令行工具的作者提供给你一个格式字符串以指定他的命令行工具需要接受哪些选项。这个字符串由若干小写字母和冒号组成,其中的每个小写字母表示一个该程序接受的选项。如果该小写字母后面紧跟了一个冒号,它就表示一个带参数的选项,否则则为不带参数的选项。例如, "ab:m:" 表示该程序接受三种选项,即"-a"(不带参数),"-b"(带参数), 以及"-m"(带参数)。
  命令行工具的作者准备了若干条命令行用以测试你的程序。对于每个命令行,你的工具应当一直向后分析。当你的工具遇到某个字符串既不是合法的选项,又不是某个合法选项的参数时,分析就停止。命令行剩余的未分析部分不构成该命令的选项,因此你的程序应当忽略它们。

输入格式

  输入的第一行是一个格式字符串,它至少包含一个字符,且长度不超过 52。格式字符串只包含小写字母和冒号,保证每个小写字母至多出现一次,不会有两个相邻的冒号,也不会以冒号开头。
  输入的第二行是一个正整数 N(1 ≤ N ≤ 20),表示你需要处理的命令行的个数。
  接下来有 N 行,每行是一个待处理的命令行,它包括不超过 256 个字符。该命令行一定是若干个由单个空格分隔的字符串构成,每个字符串里只包含小写字母,数字和减号。

输出格式

  输出有 N 行。其中第 i 行以"Case i:" 开始,然后应当有恰好一个空格,然后应当按照字母升序输出该命令行中用到的所有选项的名称,对于带参数的选项,在输出它的名称之后还要输出它的参数。如果一个选项在命令行中出现了多次,只输出一次。如果一个带参数的选项在命令行中出 现了多次,只输出最后一次出现时所带的参数。

样例输入

albw:x
4
ls -a -l -a documents -b
ls
ls -w 10 -x -w 15
ls -a -b -c -d -e -l

样例输出

Case 1: -a -l
Case 2:
Case 3: -w 15 -x
Case 4: -a -b

思路:好像从这次认证开始,第三题就以题目量来恶心人了>-<,不难但是编码起来比较麻烦,思路就是直接模拟。

#include<iostream>
#include<map>
#include<sstream>
#include<string>
using namespace std;
map<string,string> Command;
int Argument[30];//0:压根没有这个选项 1:不带参数 2:带参数 

bool leaglly(string str){//判断是否是合法的字符串 
	int n=str.length(),i;
	if(!n)return false;//空字符串不是合法参数 
	for(i=0;i<n;++i){
		if((str[i]<'a'||str[i]>'z')&&str[i]!='-'&&(str[i]<'0'||str[i]>'9'))return false;
	}
	return true;
}

int main(){
	string format,str,part;
	map<string,string>::iterator iter;
	cin>>format;
	int n=format.length(),i;
	for(i=0;i<n;++i){
		if(format[i]==':'){
			Argument[format[i-1]-'a']=2;
		}
		else Argument[format[i]-'a']=1;
	}
	
	cin>>n;
	getline(cin,str);
	for(i=1;i<=n;++i){
		getline(cin,str);
		stringstream ss(str);
		ss>>part;//先把开头的命令读掉 
		Command.clear();
		while(ss>>part){
			if(part.size()==2&&part[0]=='-'&&Argument[part[1]-'a']==1){
				Command[part]="";
			} 
			else if(part.size()==2&&part[0]=='-'&&Argument[part[1]-'a']==2&&ss>>str){
				Command[part]=str;
			}else break;
		}
		cout<<"Case "<<i<<": ";
		for(iter=Command.begin();iter!=Command.end();++iter){
			cout<<" "<<iter->first<<" "<<iter->second;
		}
		cout<<endl;
	}
}

T4

问题描述

试题编号:201403-4
试题名称:无线网络
时间限制:1.0s
内存限制:256.0MB
问题描述:

问题描述

  目前在一个很大的平面房间里有 n 个无线路由器,每个无线路由器都固定在某个点上。任何两个无线路由器只要距离不超过 r 就能互相建立网络连接。
  除此以外,另有 m 个可以摆放无线路由器的位置。你可以在这些位置中选择至多 k 个增设新的路由器。
  你的目标是使得第 1 个路由器和第 2 个路由器之间的网络连接经过尽量少的中转路由器。请问在最优方案下中转路由器的最少个数是多少?

输入格式

  第一行包含四个正整数 n,m,k,r。(2 ≤ n ≤ 100,1 ≤ k ≤ m ≤ 100, 1 ≤ r ≤ 108)。
  接下来 n 行,每行包含两个整数 xi 和 yi,表示一个已经放置好的无线 路由器在 (xi, yi) 点处。输入数据保证第 1 和第 2 个路由器在仅有这 n 个路由器的情况下已经可以互相连接(经过一系列的中转路由器)。
  接下来 m 行,每行包含两个整数 xi 和 yi,表示 (xi, yi) 点处可以增设 一个路由器。
  输入中所有的坐标的绝对值不超过 108,保证输入中的坐标各不相同。

输出格式

  输出只有一个数,即在指定的位置中增设 k 个路由器后,从第 1 个路 由器到第 2 个路由器最少经过的中转路由器的个数。

样例输入

5 3 1 3
0 0
5 5
0 3
0 5
3 5
3 3
4 4
3 0

样例输出

2

思路:其实本题容易被误导,本题的表述容易让我们先入为主,先选定加几个加哪些位置的路由器,再来尝试调小,这样考虑问题会变得很复杂,实际上可以简化为一个简单的图遍历问题。我们把所有的路由器(包括可增设的)全部构建成图,然后遍历整个图,找到所有的1->2的路径,剔除掉那些路径上增设路由器个数超过k个的非法路径就可以得到答案。至于目标是不是还要保证所有的路由器连通,本题没说,我们也不需要考虑。

这样看题目就变得十分简单了,找最短路径,可以DFS+剪枝也可以BFS。

DFS + 剪枝:(可能超时)

#include<iostream>
#include<queue>
using namespace std;
const int N=110;
int n,m,k,ans=1000;
long long r;
bool visited[N];
struct Point{
	long long x,y;
	long long distance(long long px,long long py){
		return (x-px)*(x-px)+(y-py)*(y-py);
	}
}point[N*2];

void DFS(long long x,long long y,int count,int len){
	if(count>k || len>ans)return;
	if(x==point[2].x&&y==point[2].y){
		ans=min(ans,len-1);
	}
	for(int i=2;i<=n+m;++i){
		if(!visited[i]&&point[i].distance(x,y)<=r){//没有访问过并且可以达到 
			visited[i]=true;
			if(i<=n){//旧路由 
				DFS(point[i].x,point[i].y,count,len+1);
			}	
			else{//新路由 
				DFS(point[i].x,point[i].y,count+1,len+1);
			}
			visited[i]=false;
		}
	}
}


int main(){
	cin>>n>>m>>k>>r;
	r*=r;
	int i;
	long long x,y;
	for(i=1;i<=n+m;++i)cin>>point[i].x>>point[i].y;
	visited[1]=true;
	DFS(point[1].x,point[1].y,0,0);
	cout<<ans;
	return 0;
}

BFS:

#include<iostream>
#include<queue>
using namespace std;
const int N=110;
int n,m,k,ans=1000;
long long r;
bool visited[N*2];
struct Point{
	long long x,y;
}point[N*2];
struct Node{
	long long x,y;
	int count,len;
};
queue<Node> que;

long long distance(long long x,long long y,long long px,long long py){
	return (x-px)*(x-px)+(y-py)*(y-py);
}

int BFS(Point p){
	que.push(Node{p.x,p.y,0,0});
	Node temp;
	long long x,y;
	int count,len,i;
	while(!que.empty()){
		temp=que.front();
		que.pop();
		x=temp.x,y=temp.y;
		count=temp.count;
		len=temp.len;
		for(i=2;i<=n+m;++i){
			 if(!visited[i]&&distance(x,y,point[i].x,point[i].y)<=r){//没有访问过并且可以达到 
				if(i==2)return len;
				if(i>n&&count==k)continue;
				visited[i]=true;
				if(i>n){//使用了新的路由 
					que.push(Node{point[i].x,point[i].y,count+1,len+1});	
				}else{
					que.push(Node{point[i].x,point[i].y,count,len+1});	
				}
			}
		}
	}
	return 0;
}

int main(){
	cin>>n>>m>>k>>r;
	r*=r;
	int i;
	long long x,y;
	for(i=1;i<=n+m;++i)cin>>point[i].x>>point[i].y;
	visited[1]=true;
	cout<<BFS(point[1]);
	return 0;
}

T5

问题描述

试题编号:201403-5
试题名称:任务调度
时间限制:1.0s
内存限制:256.0MB
问题描述:

问题描述

  有若干个任务需要在一台机器上运行。它们之间没有依赖关系,因此 可以被按照任意顺序执行。
  该机器有两个 CPU 和一个 GPU。对于每个任务,你可以为它分配不 同的硬件资源:
  1. 在单个 CPU 上运行。
  2. 在两个 CPU 上同时运行。
  3. 在单个 CPU 和 GPU 上同时运行。
  4. 在两个 CPU 和 GPU 上同时运行。
  一个任务开始执行以后,将会独占它所用到的所有硬件资源,不得中 断,直到执行结束为止。第 i 个任务用单个 CPU,两个 CPU,单个 CPU 加 GPU,两个 CPU 加 GPU 运行所消耗的时间分别为 ai,bi,ci 和 di。
  现在需要你计算出至少需要花多少时间可以把所有给定的任务完成。

输入格式

  输入的第一行只有一个正整数 n(1 ≤ n ≤ 40), 是总共需要执行的任 务个数。
  接下来的 n 行每行有四个正整数 ai, bi, ci, di(ai, bi, ci, di 均不超过 10), 以空格隔开。

输出格式

  输出只有一个整数,即完成给定的所有任务所需的最少时间。

样例输入

3
4 4 2 2
7 4 7 4
3 3 3 3

样例输出

7

样例说明

  有很多种调度方案可以在 7 个时间单位里完成给定的三个任务,以下是其中的一种方案:
  同时运行第一个任务(单 CPU 加上 GPU)和第三个任务(单 CPU), 它们分别在时刻 2 和时刻 3 完成。在时刻 3 开始双 CPU 运行任务 2,在 时刻 7 完成。

思路:目前并没有发现什么好的思路>-<

DFS搜索:

#include<iostream>
using namespace std;
const int N=45;
int A[N],B[N],C[N],D[N],n,ans=0x7fffffff;
bool used[N];

void DFS(int now,int cpu1,int cpu2,int gpu){
	if(now==n+1){
		ans=min(ans,max(cpu1,cpu2));
	}
	for(int i=1;i<=n;++i){
		if(!used[i]){
			used[i]=true;
			DFS(now+1,cpu1+A[i],cpu2,gpu);
			DFS(now+1,cpu1,cpu2+A[i],gpu);
			DFS(now+1,cpu1+B[i],cpu2+B[i],gpu);
			DFS(now+1,cpu1+C[i],cpu2,gpu+C[i]);
			DFS(now+1,cpu1,cpu2+C[i],gpu+C[i]);
			DFS(now+1,cpu1+D[i],cpu2+D[i],gpu+D[i]);
			used[i]=false;
		}
	}
	
}

int main(){
	int i;
	cin>>n;
	for(i=1;i<=n;++i)cin>>A[i]>>B[i]>>C[i]>>D[i];
	DFS(1,0,0,0);
	cout<<ans;
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值