阿里超级码力初赛三题解

最大公倍数

描述
小栖有一个区间,他准备从中取三个数,他想知道如何取才能使得它们的最小公倍数最大 请直接告诉小栖最小公倍数是多少。

思路:
感觉很有意思的一道题。
可以想到,如果找的三个数有公约数,那么至少是成倍的下降。
显然的结论是,x和x+1互质。如果x是奇数,x和x+2互质。
那么可以想到肯定是从区间末尾抽出三个互质的数相乘结果更优。

如果 b a = 2 b-a=2 ,那么只能是这三个数没得选了。
如果 b a 3 b-a≥3 ,且 b b 为奇数,那就直接取 b , b 1 , b 2 b,b-1,b-2 ,这三个数肯定互质。
如果 b a 3 b-a≥3 ,且 b b 为偶数,且为3的倍数,那么 b b b 3 b-3 有公约数,所以你只能取 b 1 , b 2 , b 3 b-1,b-2,b-3
如果 b a 3 b-a≥3 ,且 b b 为偶数,且不为3的倍数,那么有几种取法: ( b , b 1 , b 3 ) ( b 1 , b 2 , b 3 ) (b,b-1,b-3),(b-1,b-2,b-3) 。很明显最优就是取 b , b 1 , b 3 (b,b-1,b-3)

typedef long long ll;
class Solution {
public:
    /**
     * @param a: Left margin
     * @param b: Right margin
     * @return: return the greatest common multiple
     */

    ll gcd(ll n,ll m) {
        return m == 0 ? n : gcd(m,n % m);
    }
    
    ll lcm(ll n, ll m) {
        return n * m / gcd(n,m);
    }
    
    long long greatestcommonmultiple(int a, int b) {
        ll A = a,B = b;
        if(B - A == 2) {
            return lcm(A,lcm(A + 1,A + 2));
        }
        if(B & 1) return B * (B - 1) * (B - 2);
        else {
            if(B % 3 == 0) {
                return (B - 1) * (B - 2) * (B - 3);
            } else {
                return B * (B - 1) * (B - 3);
            }
        }
        return -1;
    }
};

完美字符串

描述
定义若一个字符串的每个字符均为’1’,则该字符串称为完美字符串。给定一个只由’0’和’1’组成的字符串s和一个整数k。你可以对字符串进行任意次以下操作

选择字符串的一个区间长度不超过k的区间[l, r],将区间内的所有’0’修改成’1’,将区间内所有的’1’修改成’0’。

你最少需要多少次操作,可以将字符串s修改成一个完美字符串

思路:
可以想到肯定是修改全为0的区间最优,否则你修改到了1的部分,到时候还得再抵消这部分操作。

class Solution {
public:
    /**
     * @param s: string need to be transformed
     * @param k: minimum char can be transformed in one operation
     * @return: minimum times of transforming all char into '1'
     */
    int perfectString(string &s, int k) {
        // Write your code here.
        int n = s.size();
        int cnt = 0;
        int ans = 0;
        for(int i = 0;i < n;i++) {
            if(s[i] == '0') {
                cnt++;
            } else {
                ans += cnt / k;
                if(cnt % k) ans++;
                cnt = 0;
            }
        }
        ans += cnt / k;
        if(cnt % k) ans++;
        return ans;
    }
};

字符串游戏
描述
Alice与Bob在一起玩一个游戏。现在有一个字符串s,每个人可以选择字符串中的某一个区间和一种字符,删除这个区间内的所有该字符(需要删除字符的数量至少为1)。从Alice开始,Alice与Bob轮流进行进行这个操作,若在某个玩家删除前,字符串已经为空,则该玩家获胜。

假设Alice和Bob都会按照其最优的解法删除,Alice想知道,她是否可以获胜。

思路:
结论题,没啥意思。就是个反nim博弈

int sum[100005][30];

class Solution {
public:
    /**
     * @param s: a string for this game 
     * @return: return whether Alice can win this game
     */
    bool stringGame(string &s) {
        // Write your code here.
        memset(sum,0,sizeof(sum));
        int n = s.size();
        for(int i = 0;i < n;i++) {
            for(int j = 0;j < 26;j++) {
                sum[i + 1][j] = sum[i][j];
            }
            sum[i + 1][s[i] - 'a']++;
        }
        int ans = 0;
        int cnt1 = 0,cnt2 = 0;
        for(int i = 0;i < 26;i++) {
            ans ^= sum[n][i];
            if(sum[n][i] == 1) cnt1++;
            if(sum[n][i] >= 2) cnt2++;
        }
        if(ans != 0 && cnt2) return true;
        if(ans == 0 && cnt2 == 0) return true;
        return false;
    }
};

房屋染色

描述
有n个房子在一列直线上,现在Bob需要给房屋染色,共有k种颜色。每个房屋染不同的颜色费用也不同,Bob希望有一种染色方案使得相邻的房屋颜色不同。但Bob计算了使相邻房屋颜色不同的最小染色费用,发现花费非常高。 于是Bob想选择一个区间作为主题步行街,在这个区间中的所有房子必须染成同一个颜色。Bob觉得这样可能能降低总花费,但是也不想让这个步行街太长。Bob认为步行街的长度最多为’t’。但这个花费太难计算,Bob打算交给你来计算。你能算出将这些房子进行染色的最小花费是多少吗?

费用通过一个nxk 的矩阵给出,比如costs[0][0]表示房屋0染颜色0的费用,costs[1][2]表示房屋1染颜色2的费用。 t是一个整数,表示有最多有多少个房屋颜色相同。

房子总数: 1 <= n <= 100 颜色总数: 1 <= k <= 100 步行街长度上限: 1 <= t <= 10 房屋染色费用: 1 <= cost[i][j] <= 1000

思路:
可以想到,花费与你选了哪些房子作为步行街,这个步行街长度,当前房子颜色有关。所以以此为状态。

定义 d p [ i ] [ j ] [ k ] [ 0 / 1 ] dp[i][j][k][0/1] 代表选到了第 i i 个房子,当前房子颜色为 j j ,该房子与之前房子颜色相同连续多少个,前 i i 个房子是否用了步行街。

转移的话就是枚举当前房子颜色和之前房子颜色,枚举前一个房子连续颜色长度。
如果当前房子和之前房子颜色相同,那就可以连成一个步行街。

之所以要加第四维状态(是否用了步行街),是因为步行街只能用一个,所以如果之前房子颜色相同长度只有一个,且要把当前房子和之前房子连起来,那么条件是之前房子之前没有出现过步行街。

const int INF = 0x3f3f3f3f;
class Solution {
public:
    /**
     * @param costs: costs of paint ith house into color j
     * @param t: maximum length of street
     * @return: minimum costs of painting all houses
     */
    int dp[105][105][15][2];
    int paintHouseIII(vector<vector<int>> &costs, int t) {
        // Write your code here.
        memset(dp,INF,sizeof(dp));
        int n = costs.size();
        int k = costs[0].size();
        for(int i = 0;i < k;i++) dp[0][i][1][0] = 0;
        
        for(int i = 0;i < n;i++) {
            for(int pre = 0;pre < k;pre++) { //上一个房子的颜色
                for(int now = 0;now < k;now++) { //当前房子的颜色
                    for(int len = 1;len <= t;len++) { //当前房子连续的长度
                        if(pre == now) { //叠加
                            if(len == 1) {
                                dp[i + 1][now][len + 1][1] = min(dp[i + 1][now][len + 1][1],dp[i][pre][len][0] + costs[i][now]);
                            } else {
                                dp[i+1][now][len + 1][1] = min(dp[i+1][now][len + 1][1],dp[i][pre][len][1] + costs[i][now]);
                            }
                        } else { //不叠加
                            dp[i + 1][now][1][1] = min(dp[i+1][now][1][1],dp[i][pre][len][1]+costs[i][now]);
                            dp[i + 1][now][1][0] = min(dp[i + 1][now][1][0],dp[i][pre][len][0]+costs[i][now]);
                        }
                    }
                }
            }
        }
        
       
        int ans = INF;
        for(int i = 0;i < k;i++) {
            for(int j = 1;j <= t;j++) {
                ans = min(ans,min(dp[n][i][j][0],dp[n][i][j][1]));
            }
        }
        
        return ans;
    }
};

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/108423873