360春招笔试算法题题解

A. 跑步(水)

题目描述

小明同学喜欢体育锻炼,他常常去操场上跑步。跑道是一个圆形,在本题中,我们认为跑道是一个半径为R的圆形,设圆心的坐标为原点(0,0)。
小明跑步的起点坐标为(R,0),他沿着圆形跑道跑步,而且一直沿着一个方向跑步。回到家后,他查看了自己的计步器,计步器显示他跑步的总路程为L。
小明想知道自己结束跑步时的坐标,但是他忘记自己是沿着顺时针方向还是逆时针方向跑的了。他想知道在这两种情况下的答案分别是多少。

题目解析:

我们都知道, L=Rθ ,那么求出 θ 来, x=rcosθ,y=rsinθ 就好了。再把y反过来

#include 
using namespace std;

#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define INF 0x3f3f3f3f
#define CLEAR(x) memset(x,0,sizeof(x))
#define pb push_back
#define mp make_pair

typedef long long ll;
typedef vector<int> vi;
typedef vector vl;
typedef pair<int,int> pii;

const int M = 1e9+7;
const double PI = acos(-1.0);
const double eps = 1e-8;

int main() {
    //RE("in.txt");WR("out.txt");
    int l,r;
    cin>>l>>r;
    double theta = l*1.0/r;
    double x=r*cos(theta),y=r*sin(theta);
    printf("%.3lf %.3lf\n%.3lf %.3lf\n",x,-y,x,y);
}

B. 剪气球串(状压DP)

题目描述

小明买了一些彩色的气球用绳子串在一条线上,想要装饰房间,每个气球都染上了一种颜色,每个气球的形状都是各不相同的。我们用1到9一共9个数字表示不同的颜色,如12345则表示一串5个颜色各不相同的气球串。但小明希望得到不出现重复颜色的气球串,那么现在小明需要将这个气球串剪成多个较短的气球串,小明一共有多少种剪法?如原气球串12345的一种是剪法是剪成12和345两个气球串。
注意每种剪法需满足最后的子串中气球颜色各不相同(如果满足该条件,允许不剪,即保留原串)。两种剪法不同当且仅当存在一个位置,在一种剪法里剪开了,而在另一种中没剪开。详见样例分析。

题目解析

其实这题的意思就是,把一个数组切成若干子数组,要求每一段里面都没有重复数字,问有多少切法。

观察到一共就9种数字,且第i个数字只与最后一段有关系,考虑 状压dp。令dp[i][s]表示前i个数,最后一串的状态为s的切法。那么,最终答案可表示为:

s=1512dp[n][s]

然后在推第i个球时考虑两种情况,一是这个球自己成为一段(这时候是无条件的),二是这个球和前面一段绑在一起(要求第i个球与前面一段没有重复颜色),由此写出转移方程:
dp[i][1<<(a[i]1)]=s=1512dp[i1][s]

dp[i][s|j]=dp[i1][j](j=1<<(a[i]1),s&j==0)

转移方程写对了,代码自然是水到渠成。

Question: 标程的解法看起来像O(n^2)的,为什么比我的快?

#include 
using namespace std;

#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define INF 0x3f3f3f3f
#define CLEAR(x) memset(x,0,sizeof(x))
#define pb push_back
#define mp make_pair

typedef long long ll;
typedef vector<int> vi;
typedef vector vl;
typedef pair<int,int> pii;

const int M = 1e9+7;
const double PI = acos(-1.0);
const double eps = 1e-8;

int n;
int a[100005];
int dp[100005][513];
int main() {
    //RE("in.txt");WR("out.txt");
    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>a[i];
    }
    dp[1][1<<(a[1]-1)]=1;
    for(int i=2;i<=n;i++) {
        int s=1<<(a[i]-1);
        for(int j=1;j<=512;j++) {
            dp[i][s]+=dp[i-1][j];
            dp[i][s]%=M;
            if(!(s&j))
                dp[i][s|j]=dp[i-1][j];
        }
    }

    ll ans=0;
    for(int i=1;i<=512;i++) {
        ans+=dp[n][i];
    }
    cout<

C.分金子(区间DP)

题目描述

A、B两伙马贼意外地在一片沙漠中发现了一处金矿,双方都想独占金矿,但各自的实力都不足以吞下对方,经过谈判后,双方同意用一个公平的方式来处理这片金矿。处理的规则如下:他们把整个金矿分成n段,由A、B开始轮流从最左端或最右端占据一段,直到分完为止。
马贼A想提前知道他们能分到多少金子,因此请你帮忙计算他们最后各自拥有多少金子?(两伙马贼均会采取对己方有利的策略)

题目解析:

这道题临场的时候没看懂题意,直接交卷了QAQ.

就是给你n段数,A,B两人轮流选择,可以选最左的或者最右的,每次都选择对己方最有利的策略。问AB最后各有多少金子?

令dp[i][j]表示当前还剩的金矿是区间[i,j]时,A取得的最大收益。这个转移要考虑此时该A拿还是该B拿。

如果该A拿,则有dp[i][j]=max(dp[i+1][j]+a[i],dp[i][j-1]+a[j]);如果该B拿,则有dp[i][j]=min(dp[i+1][j],dp[i][j-1]);,因为B拿哪边都不会增加A的收益,而他要使A尽量拿的小。

那么如何判断该A拿还是该B拿呢,这就与n和区间长度的奇偶关系有关了。观察到,如果n是偶数,则区间长度是偶数时轮到A拿,否则轮到B拿,n是奇数反之。所以该谁拿取决于n和当前区间长度的奇偶关系。在这里因为大区间只依赖于长度比他小1的小区间,所以自底向上枚举区间长度即可。

#include 
using namespace std;

#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
#define INF 0x3f3f3f3f
#define CLEAR(x) memset(x,0,sizeof(x))
#define pb push_back
#define mp make_pair

typedef long long ll;
typedef vector<int> vi;
typedef vector vl;
typedef pair<int,int> pii;

const int M = 1e9+7;
const double PI = acos(-1.0);
const double eps = 1e-8;
int dp[555][555];
int T,n;
int a[555];

int main() {
    //RE("in.txt");WR("out.txt");
    cin>>T;
    for(int i=1;i<=T;i++) {
        cin>>n;
        int sum = 0;
        for(int i=0;icin>>a[i];
            sum+=a[i];
        }
        memset(dp,0,sizeof(dp));
        //dfs(0,n-1,1);
        if(n&1) {
            for(int i=0;ifor(int i=1;ifor(int j=0;j+iif(((n+i)&1)==0) {
                    dp[j][j+i]=min(dp[j+1][j+i],dp[j][j+i-1]);
                }
                else {
                    dp[j][j+i]=max(dp[j+1][j+i]+a[j],dp[j][j+i-1]+a[j+i]);
                }
            }
        }
        cout<<"Case #"<": "<0][n-1]<<" "<0][n-1]<

你可能感兴趣的:(360春招笔试算法题题解)