目录:
1、A【树相关】 2、B【找规律】 3、C【贪心】【拓扑排序】
A、
描述(A 输入文件 : A.input 输出文件 : A.output)
一个城市的构成是一颗n 个节点的树(2 ≤ n ≤ 200), 现在需要在树中找出两条不相交的路
径(即两条路径不能有重边也不能有重点),使得路径的长度的乘积最大。
输入描述
第一行一个数n 表示这个城市一共有 n 个节点。
接下来 n-1 行,每行两个数ai 和bi (1 ≤ ai,bi ≤ n ),分别表示从ai 到bi,有一条边,每条边
的长度为1。
输出描述
输出一行一个数表示两条路径长度最大的乘积。
样例数据
样例输入1:
7
1 2
1 3
1 4
1 5
1 6
1 7
样例输出1:
0
样例输入2:
6
1 2
2 3
2 4
5 4
6 4
样例输出2:
4
B、
描述(B 输入文件 : B.input 输出文件 : B.output)
有n 个人需要看医生, 其中第i 个人需要看医生的次数是ai, 一开始从1 到n 依次排列组成
一个等待队伍, 每个人依次看医生, 那么每个人看完医生有两种情况, 第一种情况:他
已经看够他需要看医生的次数,那么他会离开。第二种情况:他还需要看医生,那么他就
会选择重新站在队伍的最后。选择医生想知道,当他看过k 次病人之后,这个等待队伍是
什么样。
输入描述
第一行两个正整数 n 和 k (1 ≤ n ≤ 105, 0 ≤ k ≤ 1014)
第二行一共个n 正整数 a1, a2, ..., an (1 ≤ ai ≤ 109),用空格隔开。
输出描述
一行,按队列的顺序输出需要的结果,每两个数之间用空格隔开,注意不要输出多余的空
格。数据保证此时队列里至少有一个人。
样例数据
样例输入1:
3 3
1 2 1
样例输出1:
2
样例输入2:
7 10
1 3 3 1 2 3 1
样例输出2:6 2 3
C、
描述(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 的时间。现
在你可以选择你一开始的房间,问完全所有任务的最短时间是多少,保证可以完成。
输入描述
第一行一个数 n (1 ≤ n ≤ 200) 。
第二行 n 个数, 第i 个数 ci (1 ≤ ci ≤ 3) 表示该任务所在的房间。.
接下来一共 n 行. 第 i 行的第一个数是 ki (0 ≤ ki ≤ n - 1),表示完成第i 个任务之前需要完
成的任务个数,之后 ki 个正整数表示需要提前完成的任务的编号。
输出描述
输出一个正整数表示完成任务需要是时间。
样例数据
样例输入1:
5
2 2 1 1 3
1 5
2 5 1
2 5 4
1 5
0
样例输出1:
7
解题报告:
第一题:先拿到到这道题的时候就想,这又是关于树的,没仔细想,肯定做不出来,就跳过写第二题了。结果后来写了这道题还得了70分,用了两个搜索回溯,找了所有边所以T了三组。正解:枚举边,以边的左右两点的子树找直径。什么是树的直径?就是这棵树的最长的一条路径。怎么搜?先从这个点出发找一条最长的路径,再从这条路径到的点搜一条最长的路径便是。所以有两个bfs,两个点就是4个dfs。要注意第二次dfs的时候不要越过所枚举的边的点找到另一棵子树上了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=205; 7 int n,ans,a1,now; 8 struct node{ 9 int l,r; 10 }; 11 int tot,he[maxn],ne[maxn*2],to[maxn*2]; 12 node bian[maxn]; 13 void add(int a,int b) 14 { 15 tot++; 16 to[tot]=b; 17 ne[tot]=he[a]; 18 he[a]=tot; 19 } 20 void dfs1(int x,int fa,int len) 21 { 22 for (int i=he[x];i;i=ne[i]) 23 if (to[i]!=fa) 24 dfs1(to[i],x,len+1); 25 if (len>=a1) a1=len; 26 if (len==a1) now=x; 27 } 28 void dfs2(int x,int fa,int len,int cant)//find the longest road 29 { 30 for (int i=he[x];i;i=ne[i]) 31 if (to[i]!=fa&&to[i]!=cant) 32 dfs2(to[i],x,len+1,cant); 33 if (len>=a1) a1=len; 34 } 35 int do_it(int x,int fa) 36 { 37 a1=0;now=0; 38 dfs1(x,fa,0); 39 dfs2(now,fa,0,fa); 40 return a1; 41 } 42 int main() 43 { 44 freopen("A.input","r",stdin); 45 freopen("A.output","w",stdout); 46 cin>>n; 47 for (int i=1;i<n;i++) 48 { 49 scanf("%d%d",&bian[i].l,&bian[i].r); 50 add(bian[i].l,bian[i].r); 51 add(bian[i].r,bian[i].l); 52 } 53 for (int i=1;i<n;i++) 54 ans=max(ans,do_it(bian[i].l,bian[i].r)*do_it(bian[i].r,bian[i].l)); 55 cout<<ans; 56 return 0; 57 }
第二题:先开始看这道题的时候觉得是这三道里面最简单的,结果果然不能轻敌,忘了队列长度会变,就全W了。正解:找规律,先从小到大排序,然后减去相同的一段数的值,直到k减不够。总之啊,遇到这类题,就是多画多手算,找规律,不要嫌麻烦而放弃。注意一些细节的问题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define ll long long 6 using namespace std; 7 const int maxn=100005; 8 int n,same[maxn]; 9 ll k; 10 struct node{ 11 int num; 12 ll ci; 13 }; 14 node a[maxn]; 15 const int comp1(const node &a,const node&b) 16 { 17 return a.ci<b.ci; 18 } 19 const int comp2(const node &a,const node&b) 20 { 21 return a.num<b.num; 22 } 23 int main() 24 { 25 freopen("B.input","r",stdin); 26 freopen("B.output","w",stdout); 27 cin>>n>>k; 28 for (int i=1;i<=n;i++) 29 { 30 scanf("%d",&a[i].ci); 31 a[i].num=i; 32 } 33 sort(a+1,a+1+n,comp1); 34 ll cnt=0,last=0,another=0; 35 for (int i=1;i<=n;i++)//预处理 36 if (a[i].ci!=a[i-1].ci) 37 same[cnt++]=i; 38 cnt=0; 39 while (k) 40 { 41 ll x=n-same[cnt]+1,y=a[same[cnt]].ci-last; 42 k-=(ll)(a[same[cnt]].ci-last)*(n-same[cnt]+1); 43 if (k>=0) 44 { 45 last=a[same[cnt]].ci; 46 cnt++; 47 } 48 else 49 { 50 k+=(ll)(a[same[cnt]].ci-last)*(n-same[cnt]+1); 51 last+=k/(n-same[cnt]+1); 52 k%=(n-same[cnt]+1); 53 break ; 54 } 55 } 56 sort(a+same[cnt],a+n+1,comp2); 57 for (int i=same[cnt];i<=n;i++) 58 a[i].ci-=last; 59 for (int i=same[cnt]+k;i<=n;i++) 60 if (a[i].ci) printf("%d ",a[i].num); 61 for (int i=same[cnt];i<same[cnt]+k;i++) 62 { 63 a[i].ci-=1; 64 if (a[i].ci) printf("%d ",a[i].num); 65 } 66 return 0; 67 }
第三题:考试的时候想这个心情很浮躁,就不想深入思考。正解:拓扑排序,在学这个的时候就没有好好学,现在全忘了,还好有这道题,又复习了一遍:就是在队列中取出入度为0的点,并删除此点及以此点为起点的所有关联边,减入度,再把入度为0的push进队列。而这道题,虽然还有路径长度为2的边,但是可以发现,其实走长度为2的边与走长度为1的边到某个点的耗费是一样的,所以可以直接简化为只有长度为1的边来走。每个房间都有一个队列表示这个房间内入度为0即可以走的边,每走一次ans++,再走下一个房间,一直跑圈,直到所有任务都完成。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 using namespace std; 5 const int maxn=205; 6 int n; 7 int ci[maxn],indgr[maxn],indgr2[maxn];//入度 8 int tot,he[maxn],ne[maxn*maxn],to[maxn*maxn];//maxn*maxn 9 queue<int> q[4]; 10 void add(int a,int b) 11 { 12 tot ++; 13 ne[tot]=he[a]; 14 to[tot]=b; 15 he[a]=tot; 16 } 17 int tuopu(int x)//每个房间一个队列,嵌套拓扑排序,选入度为0的开始 18 { 19 for (int i=1;i<=n;i++) 20 { 21 indgr2[i]=indgr[i]; 22 if (indgr2[i]==0) 23 q[ci[i]].push(i); 24 } 25 int cnt=n,ans=0; 26 while (cnt){ 27 while (!q[x].empty()){ 28 int now=q[x].front(); 29 q[x].pop(); 30 cnt--; 31 for (int i=he[now];i;i=ne[i]) 32 { 33 indgr2[to[i]]--; 34 if (indgr2[to[i]]==0) q[ci[to[i]]].push(to[i]); 35 } 36 } 37 if (cnt){ 38 ans++; 39 x++; 40 if (x>3) x=1; 41 } 42 } 43 return ans+n; 44 } 45 int main() 46 { 47 freopen("C.input","r",stdin); 48 freopen("C.output","w",stdout); 49 cin>>n; 50 for (int i=1;i<=n;i++) 51 scanf("%d",&ci[i]); 52 for (int i=1;i<=n;i++) 53 { 54 scanf("%d",&indgr[i]); 55 for (int j=1;j<=indgr[i];j++) 56 { 57 int x; 58 scanf("%d",&x); 59 add(x,i); 60 } 61 } 62 printf("%d",min(tuopu(1),min(tuopu(2),tuopu(3)) ) ); 63 return 0; 64 }
总的来说,需要注意的是,在考试的时候,千万不要出神,静下心来慢慢想,不要自我放弃,相信自己,就一定可以。每次把自己的问题弄懂,把每一道题弄懂再做,自己编。想题的时候,从最简单的开始分析,逐步深入,确保每一步分析都没有错。