zoukankan      html  css  js  c++  java
  • NOIP2001提高组题解

    (D1T1) 一元三次方程求解 ((OK))

    (D1T2) 数的划分 ((OK))

    (D1T3) 统计单词个数 ((OK))

    (D1T4) Car的旅行路线 ((OK))

    (D1T5) 挖地雷 ((OK))

    (D1T6) 砝码称重 ((OK))

    完结撒花(???)

    (T1)二分答案,然后因为题目说了根与根之差的绝对值大于等于(1),又已知值域为([-100,100]),所以每次二分([i,i+1])判断答案是否在这个区间里面即可.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    int ans;double Ans[5];
    double a,b,c,d,eps=1e-3;
    inline double calc(double x){return (double)a*x*x*x+b*x*x+c*x+d;}
    inline void solve(double l,double r){
    	if(r-l<eps){Ans[++ans]=l;return;}
    	double mid=(l+r)/2.0;
    	if(calc(l)*calc(mid)<0)solve(l,mid);
    	if(calc(mid)*calc(r)<0)solve(mid,r);
    }
    int main(){
    	scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
    	for(int i=-100;i<=99;++i){
    		double x=calc(i),y=calc(i+1);
    		if(!x){//特判根是否恰好在端点
    			Ans[++ans]=i;
    			if(ans==3)break;
    			continue;
    		}
    		if(x*y<0){
    			double l=i,r=i+1,mid;
    			while(r-l>eps){
    				mid=(l+r)/2.0;
    				if(calc(l)*calc(mid)<0)r=mid;
    				else l=mid;
    			}
    			Ans[++ans]=l;
    			if(ans==3)break;
    		}
    	}
    	for(int i=1;i<=ans;++i)printf("%.2lf ",Ans[i]);printf("
    ");
        return 0;
    }
    
    

    (T2)爆搜就行,剪枝都不需要.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    int n,k,ans;
    inline void dfs(int last,int res,int now){
    //last:记录上一次取的值,保证划分的时候数是单调的,避免重复计算
    //res:还剩下多少没被划分
    //now:当前划分出了几个部分
    	if(now>k){
    		if(!res)++ans;
    		return;
    	}
    	for(int i=last;i<=res;++i)dfs(i,res-i,now+1);
    }
    int main(){
    	n=read(),k=read();
    	dfs(1,n,1);printf("%d
    ",ans);
        return 0;
    }
    
    

    (T3) (!!!orzDTT).设(f[i][j])表示前(i)个字符拆成(j)个部分包含的最多的单词数量.(f[i][j]=f[k][j-1]+g[k+1][j]),其中(k<i),(g[k+1][j])表示区间([k+1,j])中包含的单词数量.

    所以现在只需要考虑如何求(g[i][j]).因为题目说"当选用一个单词之后,其第一个字母不能再用",从这个入手,设(q[i][0])表示以第(i)个字符开头是否存在单词,(q[i][1])表示以第(i)个字符开头最短的单词的右边界.那么(g[i][j]=sum (q[k][0])&&((q[k][1]<=j)),i<=k<=j).其实(q[i][0])这一个状态可以省略掉.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=205;
    string s,t,now[N];
    map<string,int>Map;
    int q[N][2],g[N][N],f[N][50];
    int main(){
    	int p=read(),d=read();
    	for(int i=1;i<=p;++i)cin>>t,s+=t;
    	int n=read();
    	for(int i=1;i<=n;++i)cin>>t,Map[t]=1;
    	int m=s.size();
    	for(int i=0;i<m;++i){
    		for(int j=i;j<m;++j){
    			now[i]+=s[j];
    			if(Map[now[i]]){q[i][0]=1;q[i][1]=j;break;}
    		}
    	}
    	for(int i=0;i<m;++i)
    		for(int j=i;j<m;++j)
    			for(int k=i;k<=j;++k)if(q[k][0]&&q[k][1]<=j)++g[i][j];
    	for(int j=0;j<m;++j)f[j][1]=g[0][j];
    	for(int i=1;i<m;++i)
    		for(int j=2;j<=d;++j)
    			for(int k=0;k<i;++k)
    				if(k+1>=j-1)f[i][j]=max(f[i][j],f[k][j-1]+g[k+1][i]);
    	printf("%d
    ",f[m-1][d]);
        return 0;
    }
    
    

    (T4)数据范围很小,可以考虑建完图后直接跑最短路.现在就来想想如何建图,其实建图只需要把每个矩形的第(4)个坐标找出来即可暴力连边.

    现在它每个矩形给你了(3)个点的坐标,那么这(3)个点会构成一个直角三角形,我们只需要找到直角所在的点,即可通过矩形的优美性质得到第(4)个点的坐标了,这里我用的是向量法.即如果两个向量的数量积为(0),则这两个向量垂直.然后就可以暴力判断这(3)个点哪个点是直角所在的点了.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=105;
    const int M=100005;
    int s,t,A,B,visit[N<<2];
    int cost[N],x[N][5],y[N][5];
    int tot,head[N<<2],nxt[M],to[M];
    double w[M],dis[N<<2];
    inline double calc1(int i,int j,int k){return sqrt((x[i][j]-x[i][k])*(x[i][j]-x[i][k])+(y[i][j]-y[i][k])*(y[i][j]-y[i][k]));}
    inline double calc2(int i,int j,int k,int l){return sqrt((x[i][j]-x[k][l])*(x[i][j]-x[k][l])+(y[i][j]-y[k][l])*(y[i][j]-y[k][l]));}
    inline void add(int a,int b,double c){nxt[++tot]=head[a];head[a]=tot;to[tot]=b;w[tot]=c;}
    struct node{
    	int num;double val;
    	bool operator <(const node &x)const{
    		return val>x.val;
    	}
    }temp;
    priority_queue<node>q;
    inline void dij(){
    	temp.num=(A-1)*4+1;temp.val=0.0;q.push(temp);
    	temp.num=(A-1)*4+2;temp.val=0.0;q.push(temp);
    	temp.num=(A-1)*4+3;temp.val=0.0;q.push(temp);
    	temp.num=(A-1)*4+4;temp.val=0.0;q.push(temp);
    	memset(dis,127,sizeof(dis));
    	dis[(A-1)*4+1]=dis[(A-1)*4+2]=dis[(A-1)*4+3]=dis[(A-1)*4+4]=0.0;
    	memset(visit,0,sizeof(visit));
    	while(q.size()){
    		temp=q.top();q.pop();
    		int u=temp.num;if(visit[u])continue;visit[u]=1;
    		if((B-1)*4+1<=u&&u<=(B-1)*4+4){printf("%.1lf
    ",temp.val);return;}
    		for(int i=head[u];i;i=nxt[i]){
    			int v=to[i];
    			if(dis[v]>dis[u]+w[i]){
    				dis[v]=dis[u]+w[i];
    				temp.num=v;temp.val=dis[v];q.push(temp);
    			}
    		}
    	}
    }
    int main(){
    	int T=read();
    	while(T--){
    		s=read();t=read();A=read();B=read();
    		for(int i=1;i<=s;++i){
    			for(int j=1;j<=3;++j)x[i][j]=read(),y[i][j]=read();cost[i]=read();
    			if((x[i][1]-x[i][2])*(x[i][3]-x[i][2])+(y[i][1]-y[i][2])*(y[i][3]-y[i][2])==0){
    				x[i][4]=x[i][3]-x[i][2]+x[i][1];y[i][4]=y[i][3]-y[i][2]+y[i][1];continue;
    			}
    			if((x[i][1]-x[i][3])*(x[i][2]-x[i][3])+(y[i][1]-y[i][3])*(y[i][2]-y[i][3])==0){
    				x[i][4]=x[i][2]-x[i][3]+x[i][1];y[i][4]=y[i][2]-y[i][3]+y[i][1];continue;
    			}
    			if((x[i][2]-x[i][1])*(x[i][3]-x[i][1])+(y[i][2]-y[i][1])*(y[i][3]-y[i][1])==0){
    				x[i][4]=x[i][3]-x[i][1]+x[i][2];y[i][4]=y[i][3]-y[i][1]+y[i][2];continue;
    			}
    		}
    		tot=0;memset(head,0,sizeof(head));
    		for(int i=1;i<=s;++i){//一个城市内的高铁
    			for(int j=1;j<=4;++j){
    				for(int k=1;k<=4;++k){
    					if(j==k)continue;
    					add((i-1)*4+j,(i-1)*4+k,calc1(i,j,k)*cost[i]);
    				}
    			}
    		}
    		for(int i=1;i<=s;++i){//两个城市之间的航线
    			for(int j=1;j<=4;++j){
    				for(int k=1;k<=s;++k){
    					if(i==k)continue;
    					for(int l=1;l<=4;++l)add((i-1)*4+j,(k-1)*4+l,calc2(i,j,k,l)*t);
    				}
    			}
    		}
    		dij();
    	}
        return 0;
    }
    
    

    (T5)数据范围小到我差点以为是状压(dp),其实一般的(dp)就能解决了,设(f[i][j])表示从(i)点出发,当前走到了(j)点能够获得最多的地雷.(f[i][j]=max(f[i][j],f[i][k]+a[j])),其中(w[k][j]=1).然后因为题目还要输出路径,那么转移的时候记录一下(pre[i][j])是从哪个点(k)转移过来的即可,最后递归输出一下.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=21;
    int a[N],w[N][N],f[N][N],pre[N][N];
    inline void print(int st,int ed){
    	if(!ed)return;
    	print(st,pre[st][ed]);
    	printf("%d ",ed);
    }
    int main(){
    	int n=read();for(int i=1;i<=n;++i)a[i]=read();
    	for(int i=1;i<n;++i)
    		for(int j=i+1;j<=n;++j)w[i][j]=read();
    	for(int i=1;i<=n;++i)f[i][i]=a[i];
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n;++j){
    			if(j==i)continue;
    			for(int k=1;k<=n;++k){
    				if(j==k||!w[k][j])continue;
    				if(f[i][k]&&f[i][k]+a[j]>f[i][j]){
    					f[i][j]=f[i][k]+a[j];
    					pre[i][j]=k;
    				}
    			}
    		}
    	}
    	int ans=0,st=0,ed=0;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)
    			if(f[i][j]>ans)ans=f[i][j],st=i,ed=j;		
    	print(st,ed);printf("
    ");printf("%d
    ",ans);
        return 0;
    }
    
    

    (T6)多重背包(二进制优化)变成(01)背包做即可.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    int n,ans,a[1005],b[10],f[1005];
    int main(){
    	b[1]=1;b[2]=2;b[3]=3;b[4]=5;b[5]=10;b[6]=20;
    	for(int i=1;i<=6;++i){
    		int sum=read();
    		for(int j=1;j<=sum;j<<=1){
    			a[++n]=b[i]*j;
    			sum-=j;
    		}
    		if(sum)a[++n]=b[i]*sum;
    	}
    	f[0]=1;
    	for(int i=1;i<=n;++i)
    		for(int j=1000;j>=a[i];--j)f[j]|=f[j-a[i]];
    	for(int i=1;i<=1000;++i)if(f[i])++ans;
    	printf("Total=%d
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    浩秦的竞聘报告
    烽火2640路由器命令行手册-14-桥接配置命令
    烽火2640路由器命令行手册-13-VPDN(L2TP,PPTP,PPPOE)配置命令
    烽火2640路由器命令行手册-12-IBM网络配置命令
    烽火2640路由器命令行手册-11-IP语音配置命令
    烽火2640路由器命令行手册-10-可靠性配置命令
    烽火2640路由器命令行手册-09-拨号配置命令
    烽火2640路由器命令行手册-08-服务质量配置命令
    烽火2640路由器命令行手册-07-安全配置命令
    烽火2640路由器命令行手册-06-组播协议配置命令
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11835428.html
Copyright © 2011-2022 走看看