【42模拟测试题】【归并排序】【树规】【贪心】

题目:


强迫症
问题描述
人行道铺着两行地砖,第一行每块的长度是A/B,第二行每块的长度是X/Y。两行砖块第一块的一边是对齐的。

作为一个强迫症患者,看到这样的地砖你很不爽,于是就想知道,最少隔多少距离后两行地砖的缝隙又会对齐。
输入格式
输入第一行包含一个整数T,表示测试点组数。
接下来T行,每行两个分数,格式为A/B X/Y,两个分数中间用一个空格隔开。
输出格式
T行,每行包含一个分数(若答案为整数则输出整数),表示每组数据的答案。分数必须以最简形式输出。
样例输入
2
3/2 5/8
4/3 3/10
样例输出
15/2
12
数据范围
30%的数据A,B,X,Y<=20
70%的数据T<=10
100%的数据1<=A,B,X,Y,<=10,000,T<=100,000


手套
问题描述
你现在有N对手套,但是你不小心把它们弄乱了,需要把它们整理一下。N对手套被一字排开,每只手套都有一个颜色,被记为0~N-1,你打算通过交换把每对手套都排在一起。由于手套比较多,你每次只能交换相邻两个手套。请你计算最少要交换几次才能把手套排整齐。
输入格式
输入第一行一个N,表示手套对数。
第二行有2N个整数,描述了手套的颜色。每个数都在0~N-1之间,且每个数字都会出现恰好两次。
输出格式
一行,包含一个数,表示最少交换次数。
样例输入
2
0 1 0 1
样例输出
1
数据范围
30%的数据N≤9;
60%的数据N≤1000;
100%的数据N≤200,000。


星座
问题描述
星空中有n颗星星,有n-1对星星间被人为地连上了线,每条连线有各自的长度。所有星星被连成了一个整体。现在,你要在星系中找到一个最大的十字形星座。即,你要找到两条星星构成的路径,使得它们恰好有一颗公共星(这颗公共星不能是某条路径的端点),且两条路径的长度和最大。
左图红线表示了一个合法的十字形星座,而右图的星座并不合法。

输入格式
第一行一个数n,表示星星的数量。
接下来n行,每行3个数x,y,z,表示第x颗星星和第y颗星星间有一条连线,它的长度是z。
输出格式
一行,包含一个整数,表示最大的路径长度和。若答案不存在,输出-1。
样例输入
10
3 8 6
9 3 5
1 9 2
4 8 6
2 3 3
10 4 8
5 9 5
7 2 3
6 9 1
样例输出
33
数据范围
20%的数据n<=1000
50%的数据n<=10,000
100%的数据n<=100,000,0<=z<=1000


解题报告:

  这又是一个星期前的考试了,还记得这是我考的名次最好的一次,第四名。本来该得220的,结果第一题忘开long long了,就丢了70分。。。

  第一题,水题,找最小公倍数就可以了。但是,但是最小公倍数有可能超过int,所以必须开long long。一定要注意数据范围,失分惨重。

 1 #include<iostream>
 2 #include<cstdio>
 3 #define ll long long
 4 using namespace std;
 5 ll t;
 6 ll gcd(ll a,ll b)
 7 {
 8     return b==0?a:gcd(b,a%b);
 9 }
10 ll lcm(ll a,ll b)
11 {
12     return a*b/gcd(a,b);
13 }
14 int main()
15 {
16     freopen("tile.in","r",stdin);
17     freopen("tile.out","w",stdout);
18     cin>>t;
19     while (t)
20     {
21         ll a,b,x,y;
22         scanf("%I64d/%I64d%I64d/%I64d",&a,&b,&x,&y);
23         ll p1=lcm(b,y);
24         a*=(p1/b);x*=(p1/y);
25         ll p2=lcm(a,x);
26         ll o=gcd(p1,p2);
27         p1/=o;p2/=o;
28         if (p1==1) printf("%I64d
",p2);
29         else printf("%I64d/%I64d
",p2,p1);
30         t--;
31     }
32     return 0;
33 }
View Code

  第二题,贪心的思想可以过70 (T了3组),就是从第一个数开始,把另一个移到它右边,花费它们之间的距离的步数,是不会影响中间的数的排列顺序的,然后扫描下一个,直到每一种数字都扫描完。正解:把数组按出现顺序重新赋值组成一个数组,如:2 0 1 3 3 1 2 0 的手套,可以组成:1 (2第一次出现,tot++) 2 3 4 4(3第二次出现,第一次出现3的编号为4) 3 1 2  ,然后求这个数组的逆序对即可。

 这里要注意,对归并排序的复习。还有,要开long long。!!

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 const ll maxn=400005;
 7 ll num[maxn],a[maxn];
 8 ll b[maxn],r[maxn];
 9 ll n,ans,tot,t;
10 void mergesort (ll s,ll t)
11 {
12     if (s==t) return ;
13     ll mid=(s+t)/2;
14     mergesort(s,mid);
15     mergesort(mid+1,t);
16     ll i=s,j=mid+1,k=s;
17     while (i<=mid&&j<=t)
18     {
19         if (a[i]<=a[j])
20             r[k++]=a[i++];
21         else {
22             r[k++]=a[j++];
23             ans+=mid-i+1;    
24         }
25     }
26     while (i<=mid)
27       r[k++]=a[i++];
28     while (j<=t)
29       r[k++]=a[j++];
30     for (int i=s;i<=t;i++)
31       a[i]=r[i];
32 }
33 int main()
34 {
35     freopen("gloves.in","r",stdin);
36     freopen("gloves.out","w",stdout);
37     cin>>n;
38     n*=2;
39     for (ll i=1;i<=n;i++)
40     {
41         scanf("%I64d",&num[i]);
42         if (!b[num[i]]){
43             a[i]=++tot;
44             b[num[i]]=a[i];
45         }
46         else a[i]=b[num[i]];
47     }
48     mergesort(1,n);
49     printf("%I64d",ans);
50     return 0;
51 }
View Code

  第三题,考试的时候用优化过的暴力过了50,(T了5组),主要就是建无向图,然后找度大于等于4的点,从这个点找4条最长的road,然后相加与ans比较,最后得到答案。正解:树形规划,对于一个点,可以有两种选法,选它的儿子中最长的四条边或者选它的父亲的一条最长边和自己儿子的三条边(注意:在选父亲的最长边时不能是自己现在的这条边)从叶子节点一直向上推即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int maxn=100005;
 5 int n,ans;
 6 int to[maxn*2],ne[maxn*2],w[maxn*2],he[maxn],tot;
 7 int du[maxn],f[maxn][5],fa[maxn],s[maxn];
 8 void add(int a,int b,int c)
 9 {
10     tot++;to[tot]=b;ne[tot]=he[a];w[tot]=c;he[a]=tot;
11     //tot++;to[tot]=a;ne[tot]=he[b];w[tot]=c;he[b]=tot;
12 }
13 void comp(int x,int &a,int &b,int &c,int &d)//找最长的四条边 
14 {
15     if (x<=d) return ;
16     if (x>c) d=c;
17     else {
18         d=x;return ;
19     }
20     if (x>b) c=b;
21     else {
22         c=x;return ;
23     }
24     if (x>a) {
25         b=a;
26         a=x;
27         return ;
28     }
29     b=x;
30     return;
31 }
32 inline int dfs(int x,int dis)//inline 函数 
33 {
34     int u;bool flag=false;
35     for (int i=he[x];i;i=ne[i])
36     if (to[i]!=fa[x]){
37         fa[to[i]]=x;//当前节点的父亲 
38         s[to[i]]=w[i];//儿子到父亲的距离 
39         flag=true;
40         u=dfs(to[i],dis+w[i]);//dis是一直向下加的 
41         u-=dis;//减去现在加到的长度,就是之间的距离 
42         comp(u,f[x][0],f[x][1],f[x][2],f[x][3]);
43     }
44      if (!flag) return dis;//到叶子节点就返回这段距离 
45     return dis+f[x][0];
46 }
47 int main()
48 {
49     freopen("cross.in","r",stdin);
50     freopen("cross.out","w",stdout);
51     cin>>n;
52     for (int i=1;i<n;i++)
53     {
54         int a,b,c;
55         scanf("%d%d%d",&a,&b,&c);
56         du[a]++;du[b]++;
57         add(a,b,c);
58         add(b,a,c);
59     }
60     dfs(1,0);
61     for (int i=1;i<=n;i++)
62       if (du[i]>=4)
63       {
64            if (f[i][3]) ans=max(ans,f[i][0]+f[i][1]+f[i][2]+f[i][3]); //能从儿子中选出来 
65            int j=i,fat=fa[j],di=0;
66            while (j!=1)//向父亲,父亲的父亲,父亲的父亲的父亲(祖先)找一条最长边 
67            {
68                int t=f[fat][0];//这个祖先的最长边 
69             di+=s[j];    //加上得到当前 i 到这个 祖先的距离 
70                if (f[fat][0]==f[j][0]+s[j])//s[j]  !!not di
71                   t=f[fat][1];
72                ans=max(ans,t+f[i][0]+f[i][1]+f[i][2]+di);//祖先向下的最长边加上i到这个祖先的距离 
73                j=fa[j];//下一个 
74                fat=fa[fat];//下一个祖先 
75            }
76       }
77     cout<<ans;
78     return 0;
79 }
View Code

  加油!不要“唱歌”咯。

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