2001NOIP提高组(全解)
题目链接和说明:
一元三次方程求解(奇奇怪怪的二分)
https://www.luogu.com.cn/problem/P1024
数的划分(简单DFS)
https://www.luogu.com.cn/problem/P1025
统计单词个数(DP+字符串的处理)
https://www.luogu.com.cn/problem/P1026
Car的旅行路线(几何+最短路)
https://www.luogu.com.cn/problem/P1027
一元三次方程求解
emmmm,如果知道公式的话就算了,不知道的话就得老老实实按照题目要求来了。首先答案的区间不是很大,第二,根于根之间的间隔大于等于1,那么我们就可以每次枚举一个小区间然后在这个小区间里面进行二分。只不过要注意一些小细节。
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const double esp=0.001; double a,b,c,d; double f(double x) { return a*x*x*x+b*x*x+c*x+d; } int main() { cin>>a>>b>>c>>d; int s=0; for (int i=-100; i<=100; i++){ double l=i,r=i+1; if (s==3) break; if (fabs(f(l))<esp) { printf("%.2lf ",l); s++; continue; } if (fabs(f(r))<esp) continue;//会重复,舍弃 if (f(l)*f(r)<0) { while (r-l>=esp){ double mid=(l+r)/2; if (f(l)*f(mid)<=0) r=mid; else l=mid; } printf("%.2lf ",r); s++; } } return 0; }
数的划分
本题呢没什么好说的,就是个简单搜索,只不过需要注意一下剪枝和枚举的顺序,我们枚举的时候不需要一直枚举到n
由于之前有数的存在了,我们枚举到n-之前数之和就可以了,这样就大大缩短了时间
以下是AC代码:
#include <bits/stdc++.h> using namespace std; int ans=0; void dfs(int s,int stp,int st) { if (s==0 && stp==0){ans++; return;} if (s<=0 || stp<=0) return; for (int i=st; i<=s; i++){ if (s-i<0) return; dfs(s-i,stp-1,i); } } int main() { int n,m; cin>>n>>m; dfs(n,m,1); cout<<ans<<endl; return 0; }
统计单词个数
本题的思路和2000年的最大乘积那题是一样的,直接dp即可,我们枚举在第i位之后放入第j个划分,然后进行DP即可。但值得一提的是题目的对于单词数的定义和样例解释让我有点迷。。。于是硬要凑成答案的话只能说一个头字母不能用两次,即this中t只能是一个单词的首字母,那么对于存在的this,th,is,它最多只能包含this,is或者th,is 2个。
那么我们可以预处理每个区间的单词数,然后套个DP即可,至于怎么处理,如下所示:
int ok(int st,int ed) { string s1=s.substr(st,ed-st+1); for (int i=1; i<=line; i++) if (s1.find(word[i])==0) return 1; return 0; } for (int i=len-1; i>=0; i--) { for (int j=i; j>=0; j--) { sum[j][i]=sum[j+1][i]; if (ok(j,i)) sum[j][i]++; } }
本来很快就写完了。。。但由于求前缀和的时候sum是从0开始的,我一直忘了这点。。。然后一直WA。。。
以下是AC代码:
#include <bits/stdc++.h> using namespace std; string s,word[10]; struct node { int val,vis; node(){val=0; vis=0;} }dp[250][50]; int ans[250],sum[250][250],line; int ok(int st,int ed) { string s1=s.substr(st,ed-st+1); for (int i=1; i<=line; i++) if (s1.find(word[i])==0) return 1; return 0; } int main() { int n,m; cin>>n>>m; for (int i=1; i<=n; i++){ string s1; cin>>s1; s+=s1; } int len=s.length(); cin>>line; for (int i=1; i<=line; i++){ string s1; cin>>s1; word[i]=s1; } for (int i=len-1; i>=0; i--){ for (int j=i; j>=0; j--){ sum[j][i]=sum[j+1][i]; if (ok(j,i)) sum[j][i]++; } } if (m==1) {printf("%d\n",sum[0][len-1]); return 0;} for (int i=0; i<len-1; i++){ dp[i][1].val=sum[0][i]; dp[i][1].vis=1; for (int j=2; j<m; j++){ for (int k=j-2; k<i; k++){ if (!dp[k][j-1].vis) continue; dp[i][j].val=max(dp[i][j].val,dp[k][j-1].val+sum[k+1][i]); dp[i][j].vis=1; } } if (!dp[i][m-1].vis) continue; ans[i]=dp[i][m-1].val+sum[i+1][len-1]; } int last_ans=0; for (int i=0; i<len; i++) last_ans=max(last_ans,ans[i]); cout<<last_ans<<endl; return 0; }
Car的旅行路线
emmm,这题有点毒瘤的是只给了你三个点。。。。感觉这样也没有个题目添加什么难度来着。。。
我们对每个城市产生4个连续的点,然后计算城市内部之间的消费,接下来就是搭建每个城市的与其他所有城市的航线。然后跑个Floyd算法,然后枚举一下起点和终点就结束了。。。就只是建边的时候麻烦了点。
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=450; const double inf=1e18; struct node { int x[5],y[5],price; void absorb(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,int price_city){ x[1]=x1;y[1]=y1;x[2]=x2;y[2]=y2; x[3]=x3;y[3]=y3;x[4]=x4;y[4]=y4; price=price_city; } }a[150]; double dis[mac][mac]; void in(int &x) { int f=0; char ch=getchar(); while (ch>'9' || ch<'0') ch=getchar(); while (ch>='0' && ch<='9') f=(f<<3)+(f<<1)+ch-'0',ch=getchar(); x=f; } int qpow(int x) {return x*x;} double find_dis(int x1,int y1,int x2,int y2,int flag) { if (flag) return qpow(x1-x2)+qpow(y1-y2); return sqrt(qpow(x1-x2)+qpow(y1-y2)); } void rallway(int id,int price_city) { dis[(id-1)*4+1][(id-1)*4+2]=dis[(id-1)*4+2][(id-1)*4+1]=find_dis(a[id].x[1],a[id].y[1],a[id].x[2],a[id].y[2],0)*price_city; dis[(id-1)*4+1][(id-1)*4+3]=dis[(id-1)*4+3][(id-1)*4+1]=find_dis(a[id].x[1],a[id].y[1],a[id].x[3],a[id].y[3],0)*price_city; dis[(id-1)*4+1][(id-1)*4+4]=dis[(id-1)*4+4][(id-1)*4+1]=find_dis(a[id].x[1],a[id].y[1],a[id].x[4],a[id].y[4],0)*price_city; dis[(id-1)*4+2][(id-1)*4+3]=dis[(id-1)*4+3][(id-1)*4+2]=find_dis(a[id].x[2],a[id].y[2],a[id].x[3],a[id].y[3],0)*price_city; dis[(id-1)*4+2][(id-1)*4+4]=dis[(id-1)*4+4][(id-1)*4+2]=find_dis(a[id].x[2],a[id].y[2],a[id].x[4],a[id].y[4],0)*price_city; dis[(id-1)*4+3][(id-1)*4+4]=dis[(id-1)*4+4][(id-1)*4+3]=find_dis(a[id].x[3],a[id].y[3],a[id].x[4],a[id].y[4],0)*price_city; } void solve(int x1,int y1,int x2,int y2,int x3,int y3,int &x4,int &y4) { int l1=(int)find_dis(x1,y1,x2,y2,1); int l2=(int)find_dis(x1,y1,x3,y3,1); int l3=(int)find_dis(x2,y2,x3,y3,1); if (l1==l2+l3) x4=x1+(x2-x3),y4=y1+(y2-y3); else if (l2==l1+l3) x4=x1+(x3-x2),y4=y1+(y3-y2); else x4=x2+(x3-x1),y4=y2+(y3-y1); } void plane_way(int id1,int id2,int price_plane) { for (int i=(id1-1)*4+1; i<=id1*4; i++) for (int j=(id2-1)*4+1; j<=id2*4; j++){ int v=(id1-1)*4,u=(id2-1)*4; dis[i][j]=dis[j][i]=find_dis(a[id1].x[i-v],a[id1].y[i-v],a[id2].x[j-u],a[id2].y[j-u],0)*price_plane; } } int main() { int t,n,price_plane,st,ed; in(t); while (t--){ in(n);in(price_plane);in(st);in(ed); memset(dis,0x3f,sizeof dis); for (int i=1; i<=n; i++){ int x1,x2,x3,x4,y1,y2,y3,y4,price_city; in(x1);in(y1);in(x2);in(y2);in(x3);in(y3);in(price_city); solve(x1,y1,x2,y2,x3,y3,x4,y4); a[i].absorb(x1,y1,x2,y2,x3,y3,x4,y4,price_city); rallway(i,price_city); } for (int i=1; i<=n*4; i++) dis[i][i]=0; for (int i=1; i<n; i++) for (int j=i+1; j<=n; j++) plane_way(i,j,price_plane); int tot=n*4; for (int k=1; k<=tot; k++) for (int i=1; i<=tot; i++) for (int j=1; j<=tot; j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); int st_ct[5],ed_ct[5]; for (int i=1; i<=4; i++) st_ct[i]=(st-1)*4+i; for (int i=1; i<=4; i++) ed_ct[i]=(ed-1)*4+i; double ans=inf; for (int i=1; i<=4; i++) for (int j=1; j<=4; j++) ans=min(ans,dis[st_ct[i]][ed_ct[j]]); printf ("%.1f\n",ans); } return 0; }