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;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11835428.html