二分/分治训练总结

二分已经比较熟悉了 一本通oj都刷通了 但是宏观分治还是有所疏漏 

【1】一元三次方程求解(equ.cpp) 
有形如:ax3+bx2+cx+d=0这样的一个一元三次方程。给出该方程 
中各项的系数(a,b,c,d均为实数),并约定该方程存在三个不同实根 
(根的范围在-100至100之间),且根与根之差的绝对值≥1。 
要求由小到大依次在同一行输出这三个实根(根与根之间留有空 
格),并精确到小数点后2位。 
提示:记方程f(x)=0,若存在2个数x1和x2,且x1<x2,f(x1)*f 
(x2)<0,则在(x1,x2)之间一定有一个根。 
输入:a,b,c,d 输出:三个实根(根与根之间留有空格) 
【输入输出样例】 
输入:1 -5 -4 20 输出:-2.00 2.00 5.00
 
这个比较简单 提示明显提示了可以用二分来做 这同样也是数学上找根的方法
只要三个根 那就每找出一个根就记一次数 并注意判重 sum == 3时结束
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #define int double
 5 using namespace std;
 6 int a,b,c,d;
 7 int l = -100;
 8 int r = 100;
 9 int mid;
10 double fc(double x){
11     return a * x * x * x + b * x * x + c * x + d;
12 }//写个函数方便用
13 int chk(int x){
14     if(fc(l) * fc(x) < 0) return 1;
15     else return 0;
16 }//本题二分的精髓
17 signed sum = 0;
18 signed main(){
19     freopen("equ.in","r",stdin);
20     freopen("equ.out","w",stdout);
21     cin>>a>>b>>c>>d;
22     for(register signed i = -100;i<=100;i++){
23         if(sum == 3) return 0;//3个根结束程序
24         l = i;
25         r =  l + 1;
26         if(fc(l) == 0) printf("%.2lf ",l),sum ++;
27         else if(chk(r) == 1) {
28             while (r - l >= 0.001) {//这里的0.001要注意 是为了控制double精度
29                 mid = (l + r)/2;
30                 if (chk(mid) == 1)
31                     r = mid;
32                 else
33                     l = mid;
34             }printf("%.2lf ",r),sum ++;//每找出一个就记一次数
35         }
36     }
37     return 0;
38 }
【2】循环比赛日程表(match.cpp) 
w 【问题描述】 
w 设有N个选手进行循环比赛,其中N=2M,要求每名选手 
要与其他N-1名选手都赛一次,每名选手每天比赛一次,循 
环赛共进行N-1天,要求每天没有选手轮空。 
w 输入:M 
w 输出:表格形式的比赛安排表 
w 【样例输入】match.in 
w 3 
w 【样例输出】match.out
w 1 2 3 4 5 6 7 8 
w 2 1 4 3 6 5 8 7 
w 3 4 1 2 7 8 5 6 
w 4 3 2 1 8 7 6 5 
w 5 6 7 8 1 2 3 4 
w 6 5 8 7 2 1 4 3 
w 7 8 5 6 3 4 1 2 
w 8 7 6 5 4 3 2 1
 
这个题确实很好玩 仔细看样例就会发现 每个2^n * 2^n的矩阵都会在它的左下角/右下角被发现
例 :
1 2 3 4 5 6 7 8 
2 1 4 3 6 5 8 7 
3 4 1 2 7 8 5 6 
4 3 2 1 8 7 6 5 
5 6 7 8 1 2 3 4
6 5 8 7 2 1 4 3 
7 8 5 6 3 4 1 2 
8 7 6 5 4 3 2 1
 
这是2 * 2的矩阵 细看就会发现 这个规律同理于4 * 4 、8 * 8.。。。。。。。。
由此可得 只需每次完成一个小矩阵 进行左上右下的复制即可
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cmath>
 5 #define int long long
 6 using namespace std;
 7 const int K = 1e4 +5;
 8 int M;
 9 int a[K][K];
10 int mypow(int a,int b){
11     int ret = 1;
12     while(b){
13         if(b%2 == 1){
14             ret *= a;
15         }
16         a = a*a;
17         b >>= 1;
18 
19     }
20     return ret;
21 }//2^n的快速幂 防爆
22 void work(int k,int n){
23     if(n < 1) return;
24     if(n == 2){
25         a[k][1] = k;
26         a[k][2] = k+1;
27         a[k+1][1] = k+1;
28         a[k+1][2] = k;
29     }//构建矩阵
30     else{
31         work(k,n/2);
32         work(k+n/2,n/2);//递归地处理每一次变换
33         for(register int i = k;i <= k + n/2 - 1;i++) for(register int j = n/2 + 1;j <= n;j++) a[i][j] = a[i + n/2][j - n/2];//右下复制
34         for(register int i = k+n/2;i <= k + n - 1;i++) for(register int j = n/2 + 1;j<= n;j++) a[i][j] = a[i - n/2][j - n/2];//左下复制
35     }
36 }
37 signed main(){
38     freopen("match.in","r",stdin);
39     freopen("match.out","w",stdout);
40     cin>>M;
41     int n = mypow(2,M);
42     a[1][1] = 1;
43     work(1,n);
44     for(register int i = 1;i <= n;i++){
45         for(register int j = 1;j <= n;j++){
46             printf("%lld ",a[i][j]);
47         }
48         cout<<endl;
49     }
50     return 0;
51 }

 

posted @ 2022-03-29 17:16  November&&Rain  阅读(45)  评论(0)    收藏  举报