noip2014普及组题解

1. 珠心算测验

【问题描述】
珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练,
既能够开发智力,又能够为日常生活带来很多便利,因而在很多学校得到普及。
某学校的珠心算老师采用一种快速考察珠心算加法能力的测验方法。他随机生成一个正
整数集合,集合中的数各不相同,然后要求学生回答:其中有多少个数,恰好等于集合中另
外两个(不同的)数之和?
最近老师出了一些测验题,请你帮忙求出答案。
【输入】
输入共两行,第一行包含一个整数 n,表示测试题中给出的正整数个数。
第二行有 n 个正整数,每两个正整数之间用一个空格隔开,表示测试题中给出的正整数。
【输出】
输出共一行,包含一个整数,表示测验题答案。

【样例输入】


1 2 3 4

【样例输出】

2

【样例说明】
由 1+2=3,1+3=4,故满足测试要求的答案为 2。注意,加数和被加数必须是集合中的
两个不同的数。
【数据说明】
对于 100%的数据,3 ≤ n ≤ 100,测验题给出的正整数大小不超过 10,000。

-------------------------------------------------------------------------------

简单题,但是有很多人理解题意错误,导致0分

#include<stdio.h>

int a[101],p[101];
int main(){
	int i,j,k,m,n;
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%d",&a[i]);
	for(i=1;i<n;i++)
	     for(j=i+1;j<=n;j++)
	          if(a[i]>a[j]){
	          	k=a[i];a[i]=a[j];a[j]=k;
	          }
	int ans=0;
	for(i=1;i<n-1;i++)
	    for(j=i+1;j<n;j++)
	        for(k=j+1;k<=n;k++)
	            if(a[i]+a[j]==a[k])p[k]=1;
	for(i=1;i<=n;i++)ans+=p[i];
	printf("%d
",ans);
	return 0;
}


2、比例简化

【问题描述】在社交媒体上,经常会看到针对某一个观点同意与否的民意调查以及结果。例如,对某一观点表示支持的有 1498 人,反对的有 902 人,那么赞同与反对的比例可以简单的记为1498:902。不过,如果把调查结果就以这种方式呈现出来,大多数人肯定不会满意。因为这个比例的数值太大,难以一眼看出它们的关系。对于上面这个例子,如果把比例记为 5:3,虽然与真实结果有一定的误差,但依然能够较为准确地反映调查结果,同时也显得比较直观。现给出支持人数 A,反对人数 B,以及一个上限 L,请你将 A 比 B 化简为 A’比 B’,要求在 A’和 B’均不大于 L 且 A’和 B’互质(两个整数的最大公约数是 1)的前提下, A’/B’ ≥ A/B且 A’/B’ - A/B 的值尽可能小。【输入】输入共一行,包含三个整数 A,B,L,每两个整数之间用一个空格隔开,分别表示支持人数、反对人数以及上限。【输出】输出共一行,包含两个整数 A’,B’,中间用一个空格隔开,表示化简后的比例。

【样例输入】1498 902 10【样例输出】

5 3

【数据说明】

对于 100%的数据,1 ≤ A ≤ 1,000,000,1 ≤ B ≤ 1,000,000,1 ≤ L ≤ 100,A/B ≤ L。

-------------------------------------------------------------------------------

简单题,直接在1~L之间枚举A‘,B’,找出满足条件的最小值即可

#include<stdio.h>

int gcd(int x,int y){
	if(y==0)return x;
	else return gcd(y,x%y);
}

int main(){
	int i,j,k;
	int a,b,l,a1,b1;
	double k1,k2,k3;
	scanf("%d%d%d",&a,&b,&l);
	k1=a*1.0/b;
	k3=l*1.0;
	for(i=1;i<=l;i++)
	    for(j=1;j<=l;j++)
	        if(gcd(i,j)==1){
	        	k2=i*1.0/j;
	        	if(k2>=k1 && k2-k1<k3){
	        		a1=i;b1=j;
	        		k3=k2-k1;
	        	}
	        }
	printf("%d %d
",a1,b1);
	return 0;
}


3. 螺旋矩阵
【问题描述】
一个 n 行 n 列的螺旋矩阵可由如下方法生成:
从矩阵的左上角(第 1 行第 1 列)出发,初始时向右移动;如果前方是未曾经过的格子,
则继续前进,否则右转;重复上述操作直至经过矩阵中所有格子。根据经过顺序,在格子中
依次填入 1, 2, 3, ... , n 2 ,便构成了一个螺旋矩阵。
下图是一个 n = 4 时的螺旋矩阵。


现给出矩阵大小 n 以及 i 和 j,请你求出该矩阵中第 i 行第 j 列的数是多少。

【输入】
输入共一行,包含三个整数 n,i,j,每两个整数之间用一个空格隔开,分别表示矩阵
大小、待求的数所在的行号和列号。
【输出】
输出共一行,包含一个整数,表示相应矩阵中第 i 行第 j 列的数。

【输入】
4 2 3
【输出】
14

【数据说明】
对于 50%的数据,1 ≤ n ≤ 100;
对于 100%的数据,1 ≤ n ≤ 30,000,1 ≤ i ≤ n,1 ≤ j ≤ n。

-------------------------------------------------------------------------------

首先处理特殊情况:

1、N=1直接输出1

2、如果要找的数字在边界上,直接计算即可

中间情况

1、首先算出外围有多少数字

2、分四个方向计算数字排位

以上为最简单的模拟方法,方法直观,还有更简洁的方法自行思考

#include<stdio.h>
int min(int x,int y,int z,int u){
	int p;
	p=x<y?x:y;
	p=p<z?p:z;
	p=p<u?p:u;
	return p;
}
int main(){
	int i,j,k,m,n;
	int x,y,ans;
	scanf("%d%d%d",&n,&x,&y);
	if(n==1){puts("1");return 0;}
	k=min(x,y,n-x+1,n-y+1);	
	if(k==1){
		if(x==1)ans=y;
		else if(y==n)ans=n-1+y;
		else if(x==n)ans=(n-1)*2+(n-y+1);
		else if(y==1)ans=(n-1)*3+(n-x+1);
		printf("%d
",ans);
		return 0;
	}		
	ans=0;m=n;
	for(i=1;i<k;i++){
		ans+=(m-1)*4;
		m-=2;
	}		
	if(k==x){
		ans+=y-k+1;
	}else if(k==n-y+1){	
		ans+=m-1;
		ans+=x-k+1;
	}else if(k==n-x+1){
		ans+=(m-1)*2;
		ans+=(n-y+1)-k+1;	
	}else if(k==y){
		ans+=(m-1)*3;					
		ans+=(n-x+1)-k+1;
	}
	printf("%d
",ans);
	return 0;
}


4. 子矩阵

【问题描述】
给出如下定义:
1. 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与
列的相对顺序)被称为原矩阵的一个子矩阵。
例如,下面左图中选取第 2、4 行和第 2、4、5 列交叉位置的元素得到一个 2*3 的子矩
阵如右图所示。


2. 相邻的元素:矩阵中的某个元素与其上下左右四个元素(如果存在的话)是相邻的。
3. 矩阵的分值:矩阵中每一对相邻元素之差的绝对值之和。
本题任务:给定一个 n 行 m 列的正整数矩阵,请你从这个矩阵中选出一个 r 行 c 列的
子矩阵,使得这个子矩阵的分值最小,并输出这个分值。

【输入】
第一行包含用空格隔开的四个整数 n,m,r,c,意义如问题描述中所述,每两个整数
之间用一个空格隔开。
接下来的 n 行,每行包含 m 个用空格隔开的整数,用来表示问题描述中那个 n 行 m 列
的矩阵。
【输出】
输出共 1 行,包含 1 个整数,表示满足题目描述的子矩阵的最小分值。

【样例输入】
5 5 2 3
9 3 3 3 9
9 4 8 7 4
1 7 4 6 6
6 8 5 6 9
7 4 5 6 1
【样例输出】

6

【数据说明】
对于 50%的数据,1 ≤ n ≤ 12, 1 ≤ m ≤ 12, 矩阵中的每个元素 1 ≤ a i j ≤20;
对于 100%的数据,1 ≤ n ≤ 16, 1 ≤ m ≤ 16, 矩阵中的每个元素 1 ≤ a i j ≤1000,
1 ≤ r ≤ n, 1 ≤ c ≤ m。
------------------------------------------------------------------------------------------------

O(2^N)枚举哪些行被选中,然后用O(M^2)dp

dp状态设计:f[i][j]表示当前已选中i列,最后被选中的列的编号为j的子矩阵的最小分值。

f[i][j]= min(f[i-1][k]+calc(k, j)) | (k<j) 其中calc函数计算枚举被选中的行的情况下两列之间产生的分值

朴素转移O(M),状态数O(M^2),所以dpO(M^3),需要保存最小值来优化转移成O(M^2)


以下代码为高中某位哥哥所写(WXR)

#include       <map>  
#include       <set>  
#include     <stack>
#include     <cmath>  
#include     <ctime>  
#include     <queue>  
#include    <cstdio>  
#include    <vector>  
#include    <string>  
#include    <bitset>  
#include   <cstring>  
#include   <cstdlib>  
#include  <iostream>  
#include <algorithm>  
using namespace std;  

#ifndef unix  
    #define lld "%I64d"  
    #define llu "%I64u"  
#else  
    #define lld "%lld"  
    #define llu "%llu"  
#endif  

#define FOR(a,b,c)  for(int (a)=b;(a)<=(c);++(a))  
#define FORD(a,b,c) for(int (a)=b;(a)>=(c);--(a))  
#define FORV(a,t,b) for(vector<t>::iterator a=b.begin();a!=b.end();++a)  
#define MAX(a,b)                   a=max(a,b)  
#define MIN(a,b)                   a=min(a,b)  
#define BLA                      printf("
")  
#define pb                          push_back  
#define mp                          make_pair  
#define gc                            getchar  
#define RT                             return  
#define BB                             second  
#define AA                              first  
#define bk                              break  
#define LINF             0x3f3f3f3f3f3f3f3fll  
#define INF                        0x3f3f3f3f  
#define eps                              1e-8  
#define DINF                             1e20  

//#define Generator  

typedef long long           ll;  
typedef unsigned            ui;  
typedef unsigned long long ull;  
typedef pair<int,int>      pii;  
typedef pair<ll ,ll >      pll;  

const int MAXN= 0;  
const int MOD = 0;  

template <class T> inline void CLR(T &g)             {T t;swap(t,g);}  
template <class T> inline void CLR(T &g,int a){memset(g,a,sizeof g);}  
template <class T> inline void CPY(T &a,T &b) {memcpy(a,b,sizeof a);}  
template <class T> inline bool inr(T a,T b,T c)  {RT (a>=b && a<=c);}  
inline int acc(int a,int b)                    {RT !!(a & (1<<b-1));}  
inline int fil(int a,int b,int c)    {RT a & ~(1<<b-1) | (1<<b-1)*c;}  
  
int N, M, K, Q, R, C; 

int a[20][20];
int f[20][20];//f[i][j]表示 已经选择i列 上一列是j
int sum[20], d[20], csum[20][20];

int main(){  
	#ifndef Generator  
	#ifndef ONLINE_JUDGE  
		freopen("submatrix.in" ,"r",stdin );  
		freopen("submatrix.out","w",stdout);  
	#endif  
	#endif  
	#ifdef Generator  
		freopen("input.txt","w",stdout);  
		srand((ui)time(NULL));  
	#endif  
	int T, o=0;
	scanf("%d%d%d%d", &N, &M, &R, &C);
	FOR(i, 1, N)
		FOR(j, 1, M)
			scanf("%d", &a[i][j]);
	int ans=INF;
	FOR(i, 1, (1<<N)-1){//枚举哪些行被选入矩阵
		int cnt=0;
		FOR(j, 1, N)
			if (acc(i, j)) d[++cnt]=j;
		if (cnt != R) continue;
		FOR(j, 1, M){
			sum[j]=0;
			FOR(k, 1, R-1)
				sum[j] += abs(a[d[k]][j]-a[d[k+1]][j]);
		}
		FOR(j, 1, M)
			FOR(k, j+1, M){
				csum[j][k]=0;
				FOR(l, 1, R)
					csum[j][k] += abs(a[d[l]][j]-a[d[l]][k]);
			}
		CLR(f, 0x3f);
		f[0][0]=0;
		FOR(j, 1, C){
			FOR(k, 1, M){
				int tot=0;
				FOR(l, 0, k-1)
					MIN(f[j][k], f[j-1][l]+csum[l][k]+sum[k]);
			}
		}
		FOR(j, 1, M)
			MIN(ans, f[C][j]);
	}
	printf("%d
", ans);
	RT 0;
}




原文地址:https://www.cnblogs.com/cnyali/p/4163905.html