NOIP2001提高组题解

T1:一元三次方程求解

考察知识:二分枚举

算法难度:XX 代码实现难度:XX

Tips:'X'越多表示越难

分析:这道题给定的一元三次方程限制比较多,减少了难度,我们可以考虑枚举区间长为1的区间(-100~100),二分答案,题目提示所得比较清晰,f(l)*f(r)< 0说明开区间 (l,r) 之间有根,我们采用二分法即可

代码实现:

#include<iostream>
#include<cstdio>
using namespace std;
const double eps=10e-5;
double A,B,C,D;
double f(double x){
	return A*x*x*x+B*x*x+C*x+D;
}
#define ABS(x) ((x)>0?(x):-(x))
int main(){
	int root=0;
	cin>>A>>B>>C>>D;
	for(double k=-100;k<=100;k+=1){
		if(root==3) break;
		if(ABS(f(k)==0)) printf("%.2f ",k),root++;
		double l=k,r=k+1;
		if(f(l)*f(r)>=0) continue;//在区间(l,r)没有根 
		while(r-l>=eps){
			double mid=(l+r)/2;
			if(f(mid)*f(l)<=0) r=mid;
			else l=mid;
		}
		printf("%.2f ",l),root++;
	}
	return 0;
}

T2:数的划分

考察知识:动态规划搜索+剪枝

算法难度:XX 代码实现难度:XX

分析:这道题数据范围不大,可以考虑搜索,下面仅介绍搜索算法:

因为划分的数不考虑顺序,我们可以按照升序从1到k枚举每一个数可能的取值

剪枝:如果后面的数全部与当前数相同还大于n,则剪枝

#include<iostream>
using namespace std;
int n,k,ans;
void dfs(int cur,int pre,int left){
    if(cur==1) {ans++;return;}
    for(int i=pre;i<=left/cur;i++) 
        dfs(cur-1,i,left-i);//剪枝条件包含在对i的限制中
}
int main(){
    cin>>n>>k;
    dfs(k,1,n);
    cout<<ans<<'\n';
    return 0;
}

T3: 统计单词个数

考察知识:动态规划字符串

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

分析:按动态规划一贯套路来分析

定义状态方程:f(i,k)表示字符串str[0]..str[i]分成k组包含最多单词数

状态转移方程:f(i,k)=max:f(x-1,k-1)+g[x][i]   || k<=x<=i

其中的g[x][y]表示子串str[x]..str[y]包含单词数量

边界:f(i,1)=g[0][i]

答案:f(len-1,k)

这道题难度主要在g[i][j]的计算上,考察字符串操作,具体方法见代码calc()函数

对于状态转移方程,我们考虑用记忆化搜索,比较容易实现

注意:我的代码里用了很多容易混淆的变量名,最开始我都把k_写成了k

#include<iostream>
#include<cstdio> 
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
int p,k,s,len,g[205][205],f[205][205];
char str[205],tmp[205],wd[10][205];
bool vis[205][205];
int calc(int x,int y){
	char t[205];
	strcpy(tmp,str+x);
	tmp[y-x+1]='\0';
	int cnt=0;
	for(int i=0;i<y-x+1;i++)
	for(int j=1;j<=s;j++){//考察每个单词
		strcpy(t,tmp+i);
		t[strlen(wd[j])]='\0';
		if(strcmp(wd[j],t)==0) {cnt++;break;}
	}
	return cnt;
}
int dp(int i,int k_){
	if(vis[i][k_]) return f[i][k_];
	int ret=0;
	for(int x=k_;x<=i;x++)
		ret=max(ret,dp(x-1,k_-1)+g[x][i]);
	vis[i][k_]=true;
	return f[i][k_]=ret;
}
int main(){
    scanf("%d%d",&p,&k);
    for(int i=1;i<=p;i++){
    	scanf("%s",tmp);
    	strcat(str,tmp);
	}
	len=p*20;
	scanf("%d",&s);
	for(int i=1;i<=s;i++) scanf("%s",wd[i]);
    
	for(int i=0;i<len;i++)
	  for(int j=i;j<len;j++)
	    g[i][j]=calc(i,j);
	for(int i=0;i<len;i++)
		f[i][1]=g[0][i],vis[i][1]=true;
	
	printf("%d\n",dp(len-1,k));
	return 0;
}

T4:Car的旅行路线

考察知识:图的最短路计算几何

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

分析:这道题并不是非常难,只是稍微有点繁琐,其中根据矩形三点求另一点稍微有点麻烦

把图建好后直接用dijkstra或spfa就解决了,考虑到当时不能用STL,可以使用spfa+手工队列

代码:(dijkstra和spfa都有,所以代码显得有点长)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define inf 0x7fffffff;
#define FD(p1,p2) ((x[p1]-x[p2])*(x[p1]-x[p2])+(y[p1]-y[p2])*(y[p1]-y[p2]))
double Dist(double x_1,double y_1,double x_2,double y_2){
    return sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2));
}
struct City{
    double x[4],y[4],T;
    void in(){
        for(int i=1;i<4;i++) scanf("%lf%lf",x+i,y+i);
        scanf("%lf",&T);
        if(FD(1,2)+FD(2,3)==FD(1,3))//枚举构成直角三角形的直角所在点 
            x[0]=x[1]+x[3]-x[2],y[0]=y[1]+y[3]-y[2];
        else if(FD(1,3)+FD(2,3)==FD(1,2))
            x[0]=x[1]+x[2]-x[3],y[0]=y[1]+y[2]-y[3];
        else if(FD(1,2)+FD(1,3)==FD(2,3))
            x[0]=x[2]+x[3]-y[1],y[0]=y[2]+y[3]-y[1];
//	    printf("%lf %lf %lf\n",x[0],y[0],T);
    }
}ct[105];
struct data{
    double dist;
    int city,id;
    friend bool operator < (data A,data B){
        return A.dist>B.dist;
    }
};
int n,s,a,b;
double dis[105][4],p;
bool vis[105][4];
void spfa(){
	queue<data>q;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=s;i++)
      for(int j=0;j<4;j++)
        dis[i][j]=inf;
    for(int i=0;i<4;i++)
        dis[a][i]=0,q.push((data){0,a,i});
    int i,id_i;
    double d;
    data T;
	while(!q.empty()){
        T=q.front();q.pop();
		i=T.city,id_i=T.id,d=T.dist;
		vis[i][id_i]=false;
	 	for(int j=1;j<=s;j++) if(i!=j)
        for(int id_j=0;id_j<4;id_j++){
            double d2=Dist(ct[i].x[id_i],ct[i].y[id_i],ct[j].x[id_j],ct[j].y[id_j])*p;
            if(d+d2<dis[j][id_j]){
                dis[j][id_j]=d+d2;
                if(vis[j][id_j]) continue;
                q.push((data){dis[j][id_j],j,id_j});
            }
        }
        for(int id_j=0;id_j<4;id_j++) if(id_i!=id_j){
            double d2=Dist(ct[i].x[id_i],ct[i].y[id_i],ct[i].x[id_j],ct[i].y[id_j])*ct[i].T;
            if(d+d2<dis[i][id_j]){
                dis[i][id_j]=d+d2;
                if(vis[i][id_j]) continue;
                q.push((data){dis[i][id_j],i,id_j});
            }
        }		
	}
}
void dijkstra(){
    priority_queue<data>pq;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=s;i++)
      for(int j=0;j<4;j++)
        dis[i][j]=inf;
    for(int i=0;i<4;i++)
        dis[a][i]=0,pq.push((data){0,a,i});
    data T;
    int i,id_i;
    double d;
    while(!pq.empty()){
        T=pq.top();pq.pop();
        d=T.dist,i=T.city,id_i=T.id;
        if(vis[i][id_i]) continue;
        vis[i][id_i]=true;
        for(int j=1;j<=s;j++) if(i!=j)
        for(int id_j=0;id_j<4;id_j++){
            double d2=Dist(ct[i].x[id_i],ct[i].y[id_i],ct[j].x[id_j],ct[j].y[id_j])*p;
            if(d+d2<dis[j][id_j]){
                dis[j][id_j]=d+d2;
                pq.push((data){dis[j][id_j],j,id_j});
            }
        }
        for(int id_j=0;id_j<4;id_j++) if(id_i!=id_j){
            double d2=Dist(ct[i].x[id_i],ct[i].y[id_i],ct[i].x[id_j],ct[i].y[id_j])*ct[i].T;
            if(d+d2<dis[i][id_j]){
                dis[i][id_j]=d+d2;
                pq.push((data){dis[i][id_j],i,id_j});
            }
        }
    }
}
void build(){
    scanf("%d%lf%d%d",&s,&p,&a,&b);
    for(int i=1;i<=s;i++) ct[i].in();
}
void solve(){
    double ans=inf;
//	dijkstra();
	spfa();
	for(int i=0;i<4;i++) ans=min(ans,dis[b][i]);
    printf("%.1f\n",ans); 
}
int main(){
    scanf("%d",&n);
    while(n--){
        build();
        solve();
    }
    return 0;
}

个人总结(反思):(可以忽略

没有严格按考试来做,也就是没有限定时间,没有检查,做完直接提交

100+100+60+60=320

T3:把k_写成了k,丢了40分

T4:居然把坐标公式写错了,居然还得了60分,居然之后还查错还查了大半天?!

320(400)换算为480(600)

哎,总是会出现难以检查的错误,如果只有一次提交机会,静态查错必须得有

猜你喜欢

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