2016.8.29.第39套测试题
描述(A 输入文件 : A.input 输出文件 : A.output)
一个城市的构成是一颗n 个节点的树(2 ≤ n ≤ 200), 现在需要在树中找出两条不相交的路
径(即两条路径不能有重边也不能有重点),使得路径的长度的乘积最大。
解析:
这道题很简单,因为说了是颗无环树,然后在树上找两条线不想交,乘积最大。很容易想到,枚举边,然后从两个断点的子树分别找两条最长路径,就是最大值。找最长路径方法呢,因为数据很小,可以直接暴力出所有儿子的最深长度,然后选两个最长的相加。当然更简单的就是先找个点的最深到i,再从i搜条最长路到j,那么i,j就是最长的路径。
代码:
#include<iostream> #include<cstdio> #include<fstream> #include<cstring> #include<cmath> using namespace std; ifstream fin ("A.input"); ofstream fout ("A.output"); struct stu{ int x,y; }con[205]; int a,b,f[205][205],n,ans,g[205][205]; bool use[205]; int find(int v) { int ma=0; use[v]=false; for(int i=1;i<=n;i++) if(f[v][i]&&use[i]) {g[v][i]=1+find(i); ma=max(ma,g[v][i]);} return ma; } int main() { fin>>n; for(int i=1;i<=n-1;i++) { fin>>a>>b; f[a][b]=f[b][a]=1; con[i].x=a; con[i].y=b; } for(int i=1;i<=n-1;i++) { memset(use,true,sizeof(use)); memset(g,0,sizeof(g)); int q3=0; a=con[i].x; b=con[i].y; f[a][b]=f[b][a]=0; use[a]=use[b]=false; int q1=find(a); for(int i=1;i<=n;i++) if(g[a][i]>=q3) q1=q3,q3=g[a][i]; else if(g[a][i]>q1) q1=g[a][i];//少了这一步就少了90分 q1+=q3,q3=0; int q2=find(b); for(int i=1;i<=n;i++) if(g[b][i]>=q3) q2=q3,q3=g[b][i]; else if(g[b][i]>q2) q2=g[b][i];// q2+=q3; if(q1*q2>ans) ans=q1*q2; f[a][b]=f[b][a]=1; } fout<<ans<<endl; return 0; }
错因:
找两条最长的子路时,比第一短第二长的时候没有算进去。
描述(B 输入文件 : B.input 输出文件 : B.output)
有n 个人需要看医生, 其中第i 个人需要看医生的次数是ai, 一开始从1 到n 依次排列组成
一个等待队伍, 每个人依次看医生, 那么每个人看完医生有两种情况, 第一种情况:他
已经看够他需要看医生的次数,那么他会离开。第二种情况:他还需要看医生,那么他就
会选择重新站在队伍的最后。选择医生想知道,当他看过k 次病人之后,这个等待队伍是
什么样。
解析:
这道题也很简单,就是k的问题,k 的范围是1e14过于的大了。所以需要把k的范围降下来,可以降到1e5内。如何降,其实就是找出医生在第几轮的时候结束看病。
代码:
#include<iostream> #include<cstdio> #include<fstream> #include<algorithm> #define ll long long using namespace std; int tot,g[100005],a[100005],b[100005],n,m; struct stu{ int num; ll sum; }c[100005]; ll k,q,s; int main() { freopen("B.input","r",stdin); freopen("B.output","w",stdout); scanf("%d %I64d",&n,&k);//输入的时候用的d,我操 for(int i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+n+1); for(int i=1;i<=n;i++) if(b[i]!=b[i-1]) { tot++; c[tot].num++; c[tot].sum=b[i]; } else c[tot].num++; tot=0,m=n; while(q<k) { tot++; q=(ll)(q+(c[tot].sum-c[tot-1].sum)*m); m=m-c[tot].num; } m+=c[tot].num; k=(ll)(k-(q-(c[tot].sum-c[tot-1].sum)*(m))); s=k/m+c[tot-1].sum;//之前漏了这两部 k=k%m;//差值并不是1 for(int i=1;i<=n;i++) { if(a[i]>s&&m) { if(k<=0) {m--,printf("%d",i); if(m>0) printf(" ");} else { if(a[i]>s+1) g[0]++,g[g[0]]=i; else m--; } k--; } } for(int i=1;i<=g[0];i++) { m--,printf("%d",g[i]); if(m>0) printf(" "); } return 0; }
描述(C 输入文件 : C.input 输出文件 : C.output)
有n 个任务需要你去完成,这些任务分布在3 个不同的房间,编号为1,2,3, 其中有些任务
必须在一些其他的任务完成之后才能完成。现在知道完成一个任务需要1 的时间,现在知
从房间1 到2,2 到3,3 到1 需要1 的时间,从1 到3,2 到1,3 到2 需要2 的时间。现
在你可以选择你一开始的房间,问完全所有任务的最短时间是多少,保证可以完成。
解析:
cx用暴力,直接枚举每一个房间,不停循环,有任务做,没任务下一个。正解差不多,之说用了个拓扑排序和队列,更简单了,也是循环房间。然而我一个傻逼根本没有管房间,最后TLEl 80分...感觉今天做的好垃圾,不想多说了。
代码:
#include<iostream> #include<cstdio> #include<fstream> #include<cmath> #include<queue> #include<cstring> #define p 100000000 using namespace std; ifstream fin ("C.input"); ofstream fout ("C.output"); int n,c[205],deg[205],in[205],f[205][205],q[205],x,ans=p; int move(int a,int b) { if(b==a) return 0; if((b-a)==1||(b-a)==-2) return 1; return 2; } queue<int>que[5]; int dp(int v) { int m=0; int number=0; memcpy(deg,in,sizeof(in)); for(int i=1;i<=n;i++) if(!in[i]) que[c[i]].push(i),number++; int cur=v; while(number) { while(!que[cur].empty()) { int u=que[cur].front(); que[cur].pop(); number--; for(int i=1;i<=n;i++) if(f[u][i]) { if(--deg[i]==0) que[c[i]].push(i),number++; } } if(!number) break; m++; cur++; if(cur>3) cur-=3; } return m+n; } int main() { fin>>n; for(int i=1;i<=n;i++) fin>>c[i]; for(int i=1;i<=n;i++) { fin>>in[i]; for(int j=1;j<=in[i];j++) { fin>>x; f[i][x]=f[x][i]=1; } } for(int i=1;i<=3;i++) ans=min(ans,dp(i)); fout<<ans<<endl; return 0; }