NOIP2007提高组题解

T1:统计数字

考察知识:快速排序,map的基本操作

算法难度:XX || X 实现难度:XX || X+

分析:如果用快排的话,要用分治思想,难度稍微大一些,用map就是基本操作,难度不大

如果追求速度可以写一个快速输入函数,但是注意输入是否有负数

map版代码(含快速输入):

#include
#include
#include
using namespace std;
int T;
char ch;
void scan(int& in_){
    T=1,ch=getchar();
    while(!isdigit(ch)) {
        if(ch=='-') T=-1;
        ch=getchar();
    } in_=0;
    while(isdigit(ch)) in_=in_*10+ch-'0',ch=getchar();
    in_*=T;
}
mapmp;
map::iterator it;
int n,k;
int main(){
    scan(n);
    while(n--) scan(k),mp[k]++;
    for(it=mp.begin();it!=mp.end();it++)
        printf("%d %d\n",it->first,it->second);
    return 0;
}

T2:字符串的展开

考察知识:字符串,模拟

算法难度:XX 实现难度:XXX

分析:按照题目说的来就可以了,但是要注意审题,要考虑全面,如:

#include
#include
#include
#include
char str[105];
int p1,p2,p3;
int main(){
	scanf("%d%d%d",&p1,&p2,&p3);
	scanf("%s",str);
	for(int i=0;str[i]!='\0';i++)
	if(str[i]=='-'&&i&&str[i+1]!='\0'&&str[i-1]str[i-1];j--)
			  for(int k=1;k<=p2;k++)
			  	if(p1==1) putchar(tolower(j));
			    else if(p1==2) putchar(toupper(j));
			    else putchar('*');
		}
	}
	else putchar(str[i]);
	return 0;
}

T3:矩阵取数游戏

考察知识:高精度,区间型动态规划

算法难度:XX 实现难度:XXX

分析:

读懂题意后我们发现我们可以把矩阵拆分成n排,每排m个元素的序列,然后针对每一排序列用动态规划:

1.定义f(curi,j)表示第cur排序列[i,j]取数的最大值

2.边界:f(cur,i,i)=a[cur][i]\times 2^m

3.答案ans=\sum_{i=1}^{n}f(i,1,m)

4.状态转移方程:f(cur,i,j)=max\begin{Bmatrix} & f(cur,i+1,j)+a[cur][i]\times2^m^-^j^+^i & \\ & f(cur,i,j-1)+a[cur][j]\times2^m^-^j^+^i & \end{Bmatrix}

因为最后的答案很大,使用要用高精度

代码:

#include
#include
#include
#include
using namespace std;
struct bign{
    int a[200],len;
    bign(){a[0]=0,len=1;memset(a,0,sizeof(a));}
    void get_v(int v){
        bign(); if(!v) return; len=0;
        while(v) a[len++]=v%10,v/=10;
    }
    friend bool operator < (bign A,bign B){
        if(A.len!=B.len) return A.len=0;i--) if(A.a[i]!=B.a[i])
            return A.a[i]9) C.a[i+1]++,C.a[i]-=10;
        while(C.len>1&&C.a[C.len-1]==0) C.len--;
        return C;
    }
    friend bign operator * (bign A,int x){
        bign C;
        C.len=A.len+20;
        for(int i=0;i9) C.a[i+1]+=C.a[i]/10,C.a[i]%=10;
        while(C.a[C.len-1]==0&&C.len>1) C.len--;
        return C;
    }
    friend bign operator * (bign A,bign B){
        bign C;C.len=A.len+B.len;
        for(int i=0;i9) C.a[i+1]+=C.a[i]/10,C.a[i]%=10;
        while(C.len&&C.a[C.len-1]==0) C.len--;
        return C;
    }
    void out(bool Entr=true){
        for(int i=len-1;i>=0;i--) putchar('0'+a[i]);
        if(Entr) putchar('\n');
    }
}ans,f[82][82],b[82];
int n,m,a[100][100];
void calc(int cur){
    for(int i=1;i<=m;i++) f[i][i]=b[m]*a[cur][i];
    for(int len=2;len<=m;len++)
    for(int i=1;i<=m-len+1;i++){
        int j=len+i-1;
        f[i][j]=max(f[i+1][j]+b[m-j+i]*a[cur][i],f[i][j-1]+b[m-j+i]*a[cur][j]);
    }
    ans=ans+f[1][m];
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
        scanf("%d",&a[i][j]);
    b[0].get_v(1);
    for(int i=1;i<=m;i++) b[i]=b[i-1]+b[i-1];//预处理2^i 
    for(int i=1;i<=n;i++) calc(i);
    ans.out();
    return 0;
}

T4:树网的核

考察知识:树的基本知识,树的直径,枚举

算法难度:XXX 实现难度:XXX+

分析:题目有点乱,定义有点多,但是这道题并不难,根据题目的定义枚举可能的端点并计算偏心距就可以了

算法:

1.先找树的一条直径

2.计算直径上的每一个点到树的分支(不包含直径)的最大距离

3.枚举直径上所有距离小于s的两点,计算这一段路径的偏心距,偏心距=max(两点到直径端点(不经过这段路径)的距离,路径上每一点到这一点所在树的分支的最大距离)

参考代码:

#include
#include
using namespace std;
const int maxn=305;
struct edge{
	int to,l,next;
}e[maxn*2];
int first[maxn],np;
void add(int u,int v,int l){
	e[++np]=(edge){v,l,first[u]};
	first[u]=np;
}
int n,s,A,B,dis[maxn],dis2[maxn],fa[maxn];
bool vis[maxn];
void dfs(int i,int F,int dist,int& R){
	if(dist>dis[R]) R=i;
	dis[i]=dist,fa[i]=F;
	for(int p=first[i];p;p=e[p].next){
		int j=e[p].to;
		if(j==F) continue;
		dfs(j,i,dist+e[p].l,R);
	}
}
void dfs2(int i,int dist,int rt){
	vis[i]=true;
	dis2[rt]=max(dis2[rt],dist);
	for(int p=first[i];p;p=e[p].next){
		int j=e[p].to;
		if(!vis[j]) dfs2(j,dist+e[p].l,rt);
	}
}
void build(){
	int u,v,l;
	scanf("%d%d",&n,&s);
	for(int i=1;i

 

你可能感兴趣的:(竞赛考试,NOIP提高组历年考试)