2019 百度之星初赛第一场A、B、E、补C

A : Polynomial (HDU6668)

度度熊最近学习了多项式和极限的概念。
现在他有两个多项式 f(x)和g(x),他想知道当 x趋近无限大的时候,f(x) /g(x) 收敛于多少。

  • 高数知识,找到 f 和 g 的最大次项并比较
#include<bits/stdc++.h>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n;
    while(n--)
    {
        int t, mfxi=0, mfmi =0, mgxi = 0, mgmi =0;
        cin>>t;
        for(int i=0;i<t;i++)
        {
            int xi;
            cin>>xi;
            if(xi!=0){mfmi=i;mfxi=xi;}
        }
        for (int i = 0; i < t; i++)
        {
            int xi;
            cin >> xi;
            if (xi != 0){mgmi = i;mgxi = xi;}
        }
        if(mfmi>mgmi)cout<<"1/0"<<'\n';
        else if(mfmi<mgmi)cout<<"0/1"<<'\n';
        else {
            int zi=mfxi,mu=mgxi;
            for(int i=min(zi,mu);i>1;i--){
                if(zi%i==0&&mu%i==0)zi/=i,mu/=i;
            }
            cout<<zi<<'/'<<mu<<'\n';
        }
    }
    return 0;
}

B : Game (HDU6669)

度度熊在玩一个好玩的游戏。 游戏的主人公站在一根数轴上,他可以在数轴上任意移动,对于每次移动,他可以选择往左或往右走一格或两格。

现在他要依次完成n个任务,对于任务i,只要他处于区间[ai,bi]上,就算完成了任务。 度度熊想知道,为了完成所有的任务,最少需要移动多少次? 度度熊可以任意选择初始位置。

  • 一开始想到是贪心,读入数据后,将可重合的区间取交集,得到一组 不会重合的区间 的数据,得到初始loc后,不断根据当前区间和下一个区间的相对位置确定新的loc,尽可能地靠近下一个区间(也即每次都取区间端点)
  • 不过这样做有个坑点,如果当前得出的 从loc到最靠近下一个区间的位移 是奇数,而下一次得到的位移也是奇数的情况下,之前的贪心策略会走两次 一格的位移,而可以把这两次一格的位移合并成一次两格的位移
  • 又有坑点,要想合并两次奇数位移,需满足两个条件
    • 这两次位移是沿数轴同方向的,如果两次移动是反方向则不能合并
    • 若合并,也就是把新的loc选择在了 区间中靠近那个端点的前一个位置,故该区间不能是单元素区间
  • PS:总感觉这题好像做的麻烦了,应该有更简单的做法
#include <bits/stdc++.h>
using namespace std;

typedef pair<int,int> pr;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        vector<pr> v(n);
        for(int i=0;i<n;i++)cin>>v[i].first>>v[i].second;
        vector<pr>::iterator it;
        for(it=v.begin();it<v.end()-1;)
        {   //v[i] v[i+1]
            if (((*it).second >= (*(it + 1)).first && (*it).second <= (*(it + 1)).second) || ((*it).first <= (*(it + 1)).second && (*it).first >= (*(it + 1)).first) || ((*it).first <= (*(it + 1)).first && (*it).second >= (*(it + 1)).second))//判断两个区间是否重合
            {
                (*it).first = max((*it).first, (*(it + 1)).first);
                (*it).second = min((*it).second, (*(it + 1)).second);
                it=v.erase(it+1)-1;
                n--;
            }
            else it++;
        }
        int res=0;
        int loc=0;
        int i=0,dif;
        if (v[0].second < v[1].first)loc = v[0].second;
        if (v[0].first > v[1].second)loc = v[0].first;
        for(i=1;i<n-1;i++)
        {   //分两种情况:v[i]表示的区间在v[i+1]的左(右)边
            if(v[i].second<v[i+1].first)
            {   
                dif=v[i].second-loc;
                if(dif>0){ //判断两次位移是否同方向
                    if(dif&1&&v[i].first<v[i].second){ //满足合并条件
                        loc=v[i].second-1;
                        dif--;
                    }
                    else loc=v[i].second;
                    res+=(dif+1)/2;
                }
                else {
                    loc=v[i].second;
                    res+=(-dif+1)/2;
                }
            }
            if(v[i].first>v[i+1].second){
                dif=(v[i].first-loc);
                if(dif<0){
                    dif=-dif;
                    if(dif&1&&v[i].first<v[i].second){
                        loc=v[i].first+1;
                        dif--;
                    }
                    else loc=v[i].first;
                    res+=(dif+1)/2;
                }
                else {
                    loc=v[i].first;
                    res+=(dif+1)/2;
                }
            }
        }
        if(n!=1)
        {     //处理最后一次任务
        if(v[i].first>loc)dif=v[i].first-loc;
        else dif=loc-v[i].second;
        res+=dif/2+dif%2;
        }
        /*for (int i = 0; i < n; i++)
            cout << v[i].first << ' ' << v[i].second << endl;*/
        cout<<res<<'\n';
    }
    return 0;
}

E : Seq (HDU6672)

度度熊有一个递推式 an=(∑i=1n−1ai∗i)%n. 其中 a1=1。现给出 n,需要求 an

  • 打印1000组数据,找规律,发现以6为周期
#include <bits/stdc++.h>
using namespace std;
#define LL long long

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        LL n;
        cin>>n;
        LL res;
        //switch
        if(n%6==1)res=1+4*(n/6);
        else if(n%6==2){
            res=1+3*(n/6);
        }
        else if(n%6==3||n%6==5){
            res=0+n/6;
        }
        else if(n%6==4)res=3+6*(n/6);
        else if(n%6==0)res=3*(n/6);
        cout<<res<<'\n';
    }
    return 0;
}

C : Mindis(HDU6670)

平面上有 n 个矩形,矩形的边平行于坐标轴,现在度度熊需要操控一名角色从 A 点走到 B 点。
该角色可以上下左右移动,在恰被 k 个矩形覆盖的区域,该角色的速率为 k+1 个距离/秒(矩形覆盖区域包括边界)。

请求出 A 移动到 B 最快需要多少秒。

  • Input
    • 第一行一个整数 T (1≤T≤5) 表示数据组数。
      对于每组数据,第一行输入一个整数 n (1≤n≤200)。
      接下来 n 行每行 4 个整数 x1,y1,x2,y2 (0≤x1<x2≤1000000000,0≤y1<y2≤1000000000),分别表示矩形的左下角和右上角的坐标。
      最后一行四个整数 xa*,*ya,xb,yb ((0≤xa,xb,ya,yb≤1000000000) 代表 AB 的坐标。
  • Output
    • 对于每组数据,输出一个小数表示答案。答案保留 5 位小数
  • 数据很大,没办法简单建图,故离散化处理数据,直接存点坐标,将得到的坐标数据分x、y方向排序,去重,得到一个压缩的网格图(把某些特性一致的边压缩了),然后根据该图处理数据,得到每个点被覆盖的矩形个数(这里需要注意),因为移动只能是竖直移动或者水平移动,故需要两个不同的num二维数组垂直移动的情况下,矩形的上边界上的点不应计入,水平移动时,矩形的右边界上的点不应计入(画个图想一下怎么根据 点上覆盖的矩形数 计算时间 就明白了),这样处理完数据后,就得到目标图了,然后就是单源求最短路径问题,我这里用的是bfs+优先队列,当然也可以用其他方法求得结果。
  • 2019.8.19补,详细注释在代码中
#include<bits/stdc++.h>
using namespace std;

const int MAXN=405;//离散化处理坐标,200个矩形,400个点
const int INF=0x3f3f3f3f;
int dx[MAXN],dy[MAXN],nx,ny;//坐标点,有序,不重,index从1开始
int num_hor[MAXN][MAXN],num_ver[MAXN][MAXN];//竖直或水平方向上的点 被多少个矩形覆盖
double time_[MAXN][MAXN];//bfs得到的最短时间数组
int begin_x,end_x,begin_y,end_y;//起点和终点
bool vis[MAXN][MAXN];//bfs用的vis标记数组
int dir[4][2]={1,0,-1,0,0,-1,0,1};//方向数组

struct rectangle
{
    int x1,y1,x2,y2;
}ret[MAXN/2];//坐标
struct Node
{
    int x,y;
    bool operator<(const Node &a) const
    {   //重载运算符,定义time_[][]值较大的数据优先级较小
        return time_[x][y]>=time_[a.x][a.y];
    }
};


void bfs();

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;cin>>t;
    while(t--)
    {
        int n;cin>>n;
        nx=0,ny=0;
        for(int i=0;i<n;++i)
        {
            cin>>ret[i].x1>>ret[i].y1>>ret[i].x2>>ret[i].y2;
            dx[++nx] = ret[i].x1;dx[++nx] = ret[i].x2;
            dy[++ny] = ret[i].y1;dy[++ny] = ret[i].y2;
        }
        cin>>begin_x>>begin_y>>end_x>>end_y;
        dx[++nx]=begin_x,dx[++nx]=end_x;
        dy[++ny]=begin_y,dy[++ny]=end_y;
        sort(dx+1,dx+1+nx);
        sort(dy+1,dy+1+ny);
        nx = unique(dx+1,dx+nx+1)-dx-1;
        ny = unique(dy+1,dy+ny+1)-dy-1;//"去重",更新nx,xy
        //接下来求num_hor[],num_vir[]
        //初始化
        for(int i=1;i<=nx;++i)
            for(int j=1;j<=ny;++j)
                num_ver[i][j]=num_hor[i][j]=1;
        for(int i=0;i<n;++i)
        {
            ret[i].x1=lower_bound(dx+1,dx+nx+1,ret[i].x1)-dx;
            ret[i].x2=lower_bound(dx+1,dx+nx+1,ret[i].x2)-dx;
            ret[i].y1=lower_bound(dy+1,dy+ny+1,ret[i].y1)-dy;
            ret[i].y2=lower_bound(dy+1,dy+ny+1,ret[i].y2)-dy;
            //把坐标点数据转换为其在dx[]dy[]中对应的index
            //接下来求出 水平和垂直两种不同的移动方式下的 num数组(记录点上矩形的个数)
            for(int j=ret[i].x1;j<=ret[i].x2;++j)
            {
                for(int k=ret[i].y1;k<ret[i].y2;++k)
                    num_ver[j][k]++;
                //垂直移动的时候,矩形上边界的点不计入该矩形内
            }
            for (int j = ret[i].x1; j < ret[i].x2; ++j)
            {
                for (int k = ret[i].y1; k <= ret[i].y2; ++k)
                    num_hor[j][k]++;
                //同理,水平移动的时候,矩形右边界的点不计入该矩形内
            }
        }
        begin_x = lower_bound(dx + 1, dx + 1 + nx, begin_x) - dx;
        begin_y = lower_bound(dy + 1, dy + 1 + ny, begin_y) - dy;
        end_x = lower_bound(dx + 1, dx + 1 + nx, end_x) - dx;
        end_y = lower_bound(dy + 1, dy + 1 + ny, end_y) - dy;

        time_[begin_x][begin_y]=0;
        bfs();

        cout<<fixed<<setprecision(5)<<time_[end_x][end_y]<<'\n';
    }
    return 0;
}

void bfs()
{   //bfs+优先队列
    for(int i=1;i<=nx;++i)
        for(int j=1;j<=ny;++j)
            {time_[i][j]=INF,vis[i][j]=false;}
    priority_queue<Node, vector<Node>, less<Node>> que; 
    time_[begin_x][begin_y]=0;
    Node node;
    node.x=begin_x;node.y=begin_y;
    que.push(node);
    while(!que.empty())
    {
        int x,y;
        x=que.top().x;y=que.top().y;
        que.pop();
        if(vis[x][y])continue;
        vis[x][y]=true;
        if(x==end_x&&y==end_y)break;
        for(int i=0;i<4;++i)
        {
            int xx,yy;
            xx=x+dir[i][0];
            yy=y+dir[i][1];
            if(!xx||!yy||xx>nx||yy>ny||vis[xx][yy])
                continue;
            double tm;
            if(x==xx)//垂直移动
                tm=1.0*abs(dy[yy]-dy[y])/num_ver[x][min(y,yy)];
            else //水平移动
                tm=1.0*abs(dx[xx]-dx[x])/num_hor[min(x,xx)][y];
            if(time_[x][y]+tm<time_[xx][yy])
            {
                time_[xx][yy]=time_[x][y]+tm;
                node.x=xx,node.y=yy;
                que.push(node);
            }
        }
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值