三分算法!!!!和坑点题

 意思就是有两个传送带在xy坐标轴中,一个是a到b的传送带,一个是c到d的传送带,然后跟你3个速度,问你最短时间从a到d点。

 

三分算法与二分的区别在与二分是用一个中点求值且必须在一个单调的线段上,而三分就是在一个存在峰值的线段上通过三等分找到峰值在哪里。

 

题解:首先最短距离应该是在ab上的一个点到cd上的一个点然后在到d点,这个是最快的。但是这两个点我们不知道,要快速找出这两个点就需要用到三分算法,和二分类似。

 

首先我们先去三分a到b然后把三等分点放到c到d的三方中在c到d找出一个点让ab来到cd的距离尽可能短,判断就是ab的点来到cd的点加上到d的距离即可,一点点缩小三分范围,求出最优点;

然后又回到ab的三分循环,利用a到ab点的距离加上刚刚的最优点距离判断,看看这两个ab的三分点谁最优然后在进行变换区间,一个个全部枚举完就可以了。

代码

#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
//#define int long long
using namespace std;
const double eps=1e-8;
struct G{
    double x,y;
};
G a,b,c,d;
int p,q,r1;
double ans=1e8;
double dis(G l,G r)
{
    return sqrt((l.x-r.x)*(l.x-r.x)+(l.y-r.y)*(l.y-r.y));
}
double f(G mid ,G midmid)
{
    return dis(mid,midmid)/r1+dis(midmid,d)/q;
}
double cd(G mid1)
{
    double N;
    G l,r;
    l.x=c.x,l.y=c.y;
    r.x=d.x,r.y=d.y;
    do{
        G mid, midmid;
        mid.x = (l.x + r.x) / 2.0;
        mid.y = (l.y + r.y) / 2.0;
        midmid.x = (mid.x + r.x) / 2, 0;
        midmid.y = (mid.y + r.y) / 2.0;
        if (f(mid1, mid) < f(mid1, midmid)) {
            N=min(ans,f(mid1,mid));
            r=midmid;
        }
        else
        {
            N=min(ans,f(mid1,midmid));
            l=mid;
        }
    } while (dis(l,r)>eps);
    return N;
}
int main()
{
    cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>d.x>>d.y;
    cin>>p>>q>>r1;
    G l,r;
    l.x=a.x,l.y=a.y,r.x=b.x,r.y=b.y;
    do
    {
        G mid,midmid;
        mid.x=(l.x+r.x)/2.0;
        mid.y=(l.y+r.y)/2.0;
        midmid.x=(mid.x+r.x)/2.0;
        midmid.y=(mid.y+r.y)/2.0;
        double ans1=cd(mid);
        double ans2=cd(midmid);
        
        ans1+=dis(a,mid)/p;
        ans2+=dis(a,midmid)/p;
        if(ans1<ans2)
        {
            ans=min(ans,ans1);
            r=midmid;
        }
        else
        {
            ans=min(ans,ans2);
            l=mid;
        }
    } while (dis(l,r)>eps);
    printf("%0.2lf",ans);
    return 0;
}

 

 

题意:给4个人n个数字然后每个人选一个数字,相加起来等于0就是正确的组合,问你有多少种组合。

 穿插一个函数upper_bound(这个函数是二分可以返回第一个大与你找的数字的下标)就是二分地左边界一,lower_bound(这个是返回第一个大于或等于目标数的下标)就是二分的右边界。

 

题解:首先把两两相加,两个人的组合相加列举出来,a和b一起,c和d一起,n的2次方复杂度,然后在排序任意一个组合,枚举一个组合然后二分排序的组合找相反数即可,可能这个排序中有重复的数字,所以我们需要二分左边界和和右边界然后相减获得个数。

#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
#define int long long
using namespace std;
const int N=1e6;
struct G{
    int a,b,c,d;
}a[N];
map <int,int> c,d;
int32_t main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].a>>a[i].b>>a[i].c>>a[i].d;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            c[a[i].a+a[j].b]++;
            d[a[i].c+a[j].d]++;
        }
    }
    int sum=0;
   for(int i=1;i<=n;i++)
   {
       for(int j=1;j<=n;j++)
       {
           if(c.count(-(a[i].c+a[j].d)))
           {
               sum+=c[-(a[i].c+a[j].d)];
           }
       }
   }
    cout<<sum;
    return 0;
}

 

 题解:这个题数据比较小所以可以直接暴力去枚举,删掉行后看列如果列删掉敌人后大于给到b那就不行。

 

注意其中可能有一个地方很奇怪,,,,,就是i<(1<<m)这个代表的意思就是有2^m-1种排法,换句话说就是比如你有4行a只有2 所以你要一个个枚举先删除1   2在这个表达式中就是0011的2进制或者删除3  4就是1100的二进制所以这样写可能让你在一个循环里面枚举出2^m-1种情况然后等到他等于需要删除次数 a 后再去双 for 枚举航进行了

 

有(i>>j)&1这个是判断0011这个二进制中1的位置的也就是删除行让x++.

#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
//#define int long long
using namespace std;
const int N=1e5+5;
string s[N];
int vis[N];
int32_t main()
{
   int t;
   cin>>t;
    while (t--)
    {
        int n,m;
        int a,b;
        int f=0;
        cin>>n>>m>>a>>b;
        for(int i=0;i<n;i++)
            cin>>s[i];
        for(int i=0;i<(1<<n);i++)
        {
            int x=0;
            for(int j=0;j<n;j++)
            {
                if((i>>j)&1) x++;
            }
            if(x!=a) continue;
            int y=0;
            for(int j=0;j<m;j++)
            {
                for(int k=0;k<n;k++)
                {
                    if(s[k][j]=='*'&& !((i>>k)&1))
                    {
                        y++;
                        break;
                    }
                }
            }
            if(y<=b)
            {
                f=1;
            }
        }
        if(f)
        {
            cout<<"yes"<<endl;
        }
        else
        {
            cout<<"no"<<endl;
        }
    }
    return 0;
}

 

P3382 【模板】三分法 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

模板题:用于复习

#include <bits/stdc++.h>
//#pragma  GCC optimize("Ofast")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
//#define double long double
#define int long long
//#define endl '\n';
using namespace std;
const int N=1e5+8,M=1e1;
const int INF = 0x3f3f3f3f;
const int mod=998244353;
typedef pair<int,int> PII;

double a[N];
int n;
double ch(double x)
{
    double ans=0;
    double y=0;
    for(int i=n;i>=0;i--)
    {
        ans+=a[i]*pow(x,y);
        y++;
    }
    return ans;
}
int32_t  main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);


    double l,r;
    cin>>n>>l>>r;
    for(int i=0;i<=n;i++)
    {
        cin>>a[i];
    }
    double Lmid,Rmid;
    while (l+1e-7<=r)
    {
        Lmid=l+(r-l)/3.0;
        Rmid=r-(r-l)/3.0;
        if(ch(Lmid)>ch(Rmid)) r=Rmid;
        else l=Lmid;
    }
    cout<<(r+l)/2.0;
    return 0;
}

 

posted @ 2023-07-18 13:25  whatdo+  阅读(24)  评论(0编辑  收藏  举报