微软2016校园招聘9月在线笔试题解

微软2016校园招聘9月在线笔试题解

题目网址列表:http://hihocoder.com/contest/mstest2015sept2/problems

题目一分析:

问题描述:在二维坐标系中,给定一个圆的圆心坐标x,y,半径r,这个三个数是浮点数,在圆内或者圆边上找一个整数点(x,y坐标都是整数的点)使该点到圆心的距离最大,如果有多个这样的点,选择x坐标最大的点,如果还有多个点存在,则选择y坐标最大的点。

解体思路:通过枚举x,求y的最大值或者最小值。在程序中需要注意y值最大最小值的求法,可以调用cmath包中的函数 floor 和ceil函数,可以看下图进行理解,其中add_y就是程序中的变量。


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define eps 1e-7
using namespace std;
double x,y,r,MaxDis,res_x,res_y;
//计算两点之间的距离
double cal_dis(int x1,int y1)
{
    return sqrt((x-x1)*(x-x1) + (y-y1) * (y-y1));
}

//判断新的坐标是否更新已有的坐标
void judge(int x1,int y1)
{
    double dis = cal_dis(x1,y1);
    if(dis > MaxDis + eps)
    {
        MaxDis = dis;
        res_x = x1;
        res_y = y1;
    }
    else if(abs(dis - MaxDis) < eps)
    {
        if(x1 > res_x)
        {
            res_x = x1;
            res_y = y1;
        }
        else if(x1 == res_x && y1 > res_y)
        {
            res_y = y1;
        }
    }
}

int main()
{
    int cur_x,cur_y;
    while(cin >> x >> y >> r)
    {
        MaxDis = 0;
        // 枚举x坐标
        for(int i = -(r + 3);i<=r + 2;i++)
        {
            // 得到当前x的坐标
            cur_x = int(x) + i;
            //判断x坐标是否超出圆外
            if(abs(cur_x - x) >= r + eps)
                continue;
            double add_y = sqrt(r * r - (cur_x - x) * (cur_x - x));
            //获得最大的y轴坐标
            cur_y = floor(y + add_y);
            judge(cur_x,cur_y);
            //cout << cur_x << " ==== " << cur_y << endl;
            //获得最小的y轴坐标
            cur_y = ceil(y - add_y);
            judge(cur_x,cur_y);
            //cout << cur_x << " ==== " << cur_y << endl;
        }

        cout << res_x << " " << res_y << endl;

    }
    return 0;
}

题目二分析:
题目描述:n个城市,每条道路连接两个城市,n-1条道路正好把n个城市连接成一颗树,求解下面sum的值:

题目中有对边的权值进行改变后,再要求求解Sum,所以需要进行预处理,使对边权改变时,再次计算的时间复杂度比较低。
解题思路如下:统计每条边被上面的式子计算多少次。计算多少次,可以很容易从下图中看出来。图中绿色这条边把树分成两个子集,所以该边被计算的次数就是N(s1) * N(s2) ,其中N(s1) 表示s1集合点的个数 N(s1) = 3 ,代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

#define MAXN 100005
vectornext_vec[MAXN];
vectorvalue_vec[MAXN];

//记录每条边的长度,key是两个城市的id,id小的放前面
map,int> edge_length_map;

// 记录每条边在所有路径中使用的次数,key是两个城市的id,id小的放前面
map,LL> num_map;

int n,m;
bool used[MAXN];

//递归求解每条边被计算几次
int dfs(int id)
{
    used[id] = true;
    int acc = 1;
    int sub_num = 0;
    int sub_id;
    for(int i = 0;i< next_vec[id].size();i++)
    {
        sub_id = next_vec[id][i];
        //已经访问过不再访问
        if(used[sub_id] == true)
            continue;
        sub_num = dfs(sub_id);
        acc += sub_num;
        if(id < sub_id)
            num_map[make_pair(id,sub_id)] = (LL) sub_num * (n - sub_num);
        else
            num_map[make_pair(sub_id,id)] = (LL) sub_num * (n - sub_num);
    }
    return acc;

}
int main()
{
    while(scanf("%d%d",&n,&m) == 2)
    {
        memset(used,0,sizeof(used));
        int u,v,k;
        for(int i = 0;i v)
                swap(u,v);
            next_vec[u].push_back(v);
            next_vec[v].push_back(u);
            value_vec[u].push_back(k);
            value_vec[v].push_back(k);
            edge_length_map[make_pair(u,v)] = k;
        }

        dfs(1);

        LL sum = 0;
        map,int>::iterator ite = edge_length_map.begin();
        for(;ite!=edge_length_map.end();ite++)
        {
            sum += num_map[ite->first] * ite->second;
        }

        char command[10];
        for(int i = 0;i v)
                    swap(u,v);
                sum += (LL)(k - edge_length_map[make_pair(u,v)]) * (num_map[make_pair(u,v)]);
                edge_length_map[make_pair(u,v)] = k;
            }
        }


        for(int i = 0;i<=n;i++)
        {
            next_vec[i].clear();
            value_vec[i].clear();
        }

        edge_length_map.clear();
        num_map.clear();



    }
    return 0;
}



题目三分析:
题目大意:给定一个序列,求该序列的子序列中有多少是Fibonacci数列的前缀。

解题思路:很容易想到是动态规划,dp[i][j] 表示 序列的前i个数产生的子序列的数量(该子序列需要满足两个条件,1 是fibonacci数列的前缀 2子序列的最后一个数是fibonacci数列中的第j个)。理解状态的含义后,转移方程就比较好理解了,在此不详细说了,具体看程序。在实现中可以省略第一维,有点类似滚动数组。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

#define MAXN 1000005
#define mod 1000000007

int a[MAXN],fib[30];
LL dp[30];
map key_to_id;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i<=n;i++)
        scanf("%d",a+i);

    fib[0] =1;
    fib[1] = 1;
    int MaxNum = 2;
    int index = 2;
    while(MaxNum)
    {
        fib[MaxNum] = fib[MaxNum-1] + fib[MaxNum-2];
        key_to_id[fib[MaxNum]] = index++;
        if(fib[MaxNum] > 100005)
            break;
        MaxNum++;
    }

    memset(dp,0,sizeof(dp));

    LL sum = 0;
    int id;
    for(int i= 1;i<=n;i++)
    {
        if(a[i] == 1)
        {
            //当是1时,特殊处理
            sum = (sum + 1 + dp[0]) %mod;
            dp[1] += dp[0];
            dp[0] += 1;
            dp[1] %= mod;
            dp[0] %= mod;
        }
        else if(key_to_id.find(a[i]) != key_to_id.end())
        {
            // 是fibonacci 数列中的数时,进行如下处理
            id = key_to_id[a[i]];
            sum += dp[id -1];
            dp[id] += dp[id -1];
            dp[id] %= mod;
            sum %= mod;
        }
        // 如果不是fibonacci 数列中的数,不需要处理
    }

    cout << sum << endl;


    return 0;
}


题目四分析:

题目大意:给定两个NXN的矩阵,对其中一个矩阵进行顺时针旋转操作(旋转操作有四种,90度,180度,270度,360度顺时针旋转),当N为偶数时,把矩阵分成相同大小的四块,再进行旋转操作,一直递归到N为奇数时,问第一个矩阵能否通过旋转与第二个矩阵完全相同。

解题思路:编写一个rotate函数实现矩阵90度旋转(其他都可以由它得到),第二个就是递归的实现,记得递归时的状态还原

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;

#define MAXN 105
#define mod 1000000007

int matri_a[MAXN][MAXN],matri_b[MAXN][MAXN];
// 对二维数组中的某一正方形小块顺时针旋转90度
void Rotate(int x1,int y1,int x2,int y2)
{
    int len = x2 - x1 + 1;
    int **a = new int*[len];
    for(int i = 0 ;i< len;i++)
        a[i] = new int[len];
    for(int i = 0;i



你可能感兴趣的:(algorithm)