【模拟题(63550802...)】解题报告【贪心】【拓扑排序】【找规律】【树相关】

目录:

  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的时候不要越过所枚举的边的点找到另一棵子树上了。

 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 }
View Code

  第二题:先开始看这道题的时候觉得是这三道里面最简单的,结果果然不能轻敌,忘了队列长度会变,就全W了。正解:找规律,先从小到大排序,然后减去相同的一段数的值,直到k减不够。总之啊,遇到这类题,就是多画多手算,找规律,不要嫌麻烦而放弃。注意一些细节的问题。

 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 }
View Code

  第三题:考试的时候想这个心情很浮躁,就不想深入思考。正解:拓扑排序,在学这个的时候就没有好好学,现在全忘了,还好有这道题,又复习了一遍:就是在队列中取出入度为0的点,并删除此点及以此点为起点的所有关联边,减入度,再把入度为0的push进队列。而这道题,虽然还有路径长度为2的边,但是可以发现,其实走长度为2的边与走长度为1的边到某个点的耗费是一样的,所以可以直接简化为只有长度为1的边来走。每个房间都有一个队列表示这个房间内入度为0即可以走的边,每走一次ans++,再走下一个房间,一直跑圈,直到所有任务都完成。

 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 } 
View Code

  总的来说,需要注意的是,在考试的时候,千万不要出神,静下心来慢慢想,不要自我放弃,相信自己,就一定可以。每次把自己的问题弄懂,把每一道题弄懂再做,自己编。想题的时候,从最简单的开始分析,逐步深入,确保每一步分析都没有错。

原文地址:https://www.cnblogs.com/lx0319/p/5819826.html