NOIP2007提高组题解

版权声明:欢迎转载+原文章地址~ https://blog.csdn.net/Hi_KER/article/details/81707417

T1:统计数字

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

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

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

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

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

#include<cstdio>
#include<map>
#include<cctype>
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;
}
map<int,int>mp;
map<int,int>::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<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
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]){//条件要考虑全 
		if((isalpha(str[i+1])^isalpha(str[i-1]))||
		str[i+1]=='-'||str[i-1]=='-')//注意异或 
		{putchar('-');continue;}
		if(p3==1){
			for(int j=str[i-1]+1;j<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{
			for(int j=str[i+1]-1;j>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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
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<B.len;
        for(int i=A.len-1;i>=0;i--) if(A.a[i]!=B.a[i])
            return A.a[i]<B.a[i];
        return false;
    }
    friend bign operator + (bign A,bign B){
        bign C;
        C.len=max(A.len,B.len)+1;
        for(int i=0;i<C.len;i++) C.a[i]=A.a[i]+B.a[i];
        for(int i=0;i<C.len;i++) if(C.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;i<C.len;i++) C.a[i]=A.a[i]*x;
        for(int i=0;i<C.len;i++)
            if(C.a[i]>9) 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;i<A.len;i++)
          for(int j=0;j<B.len;j++)
            C.a[i+j]+=A.a[i]*B.a[j];
        for(int i=0;i<C.len;i++) if(C.a[i]>9) 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<cstdio>
#include<algorithm>
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<n;i++){
		scanf("%d%d%d",&u,&v,&l);
		add(u,v,l),add(v,u,l);
	}
	dfs(1,0,0,A=0);//找直径 
	dfs(A,0,0,B=0);
	for(int i=B;i;i=fa[i]) vis[i]=true;
	for(int i=B;i;i=fa[i]) dfs2(i,0,i);//计算分支最大距离 
}
int ans=0xffffff; 
void solve(){
	for(int i=B;i;i=fa[i])//枚举 
	for(int j=i;j;j=fa[j]) if(dis[i]-dis[j]<=s){ 
		int T=max(dis[j],dis[B]-dis[i]);
		for(int k=i;;k=fa[k]){//计算偏心距 
			T=max(T,dis2[k]);
			if(k==j) break;
		}
		ans=min(ans,T);
	}
	else break;
	printf("%d\n",n==1?0:ans);
}
int main(){
	build();
	solve();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Hi_KER/article/details/81707417