[Leetcode/c++] 第206场周赛

P1 二进制矩阵中的特殊位置

给你一个大小为 rows x cols 的矩阵 mat,其中 mat[i][j] 是 0 或 1,请返回 矩阵 mat 中特殊位置的数目 。

特殊位置 定义:如果 mat[i][j] == 1 并且第 i 行和第 j 列中的所有其他元素均为 0(行和列的下标均 从 0 开始 ),则位置 (i, j) 被称为特殊位置。

暴力

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define rep(i,a,n) for(int i=a; i<n; i++)
#define per(i,a,n) for(int i=n-1; i>=a; i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define fr(x) freopen(x,"r",stdin)
#define fw(x) freopen(x,"w",stdout)
typedef vector<int> VI;
typedef vector<char> VC;
typedef vector<VI> VVI;
typedef vector<VC> VVC;
typedef long long ll;
typedef pair<int, int> PII;
typedef unordered_map<int, int> MII;
typedef double db;
ll gcd(ll a, ll b){return b?gcd(a%b, b):a;}
const ll mod = 1000000007;
// head 
class Solution {
public:
    int numSpecial(vector<vector<int>>& mat) {
        int n = SZ(mat),m = SZ(mat[0]), s;
        VI R, C;
        rep(i, 0, n){
            s = 0;
            rep(j, 0, m) s += mat[i][j];
            if(s == 1) R.pb(i);
        }
        rep(j, 0, m){
            s = 0;
            rep(i, 0, n) s += mat[i][j];
            if(s == 1) C.pb(j);
            
        }
        int ans = 0;
        for(auto i: R) for(auto j: C)
            if(mat[i][j] == 1) ans ++; 
        return ans;

    }
};

P 统计不开心的朋友

给你一份 n 位朋友的亲近程度列表,其中 n 总是 偶数 。

对每位朋友 i,preferences[i] 包含一份 按亲近程度从高到低排列 的朋友列表。换句话说,排在列表前面的朋友与 i 的亲近程度比排在列表后面的朋友更高。每个列表中的朋友均以 0 到 n-1 之间的整数表示。

所有的朋友被分成几对,配对情况以列表 pairs 给出,其中 pairs[i] = [xi, yi] 表示 xi 与 yi 配对,且 yi 与 xi 配对。

但是,这样的配对情况可能会是其中部分朋友感到不开心。在 x 与 y 配对且 u 与 v 配对的情况下,如果同时满足下述两个条件,x 就会不开心:

x 与 u 的亲近程度胜过 x 与 y,且
u 与 x 的亲近程度胜过 u 与 v
返回 不开心的朋友的数目 。

模拟

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define rep(i,a,n) for(int i=a; i<n; i++)
#define per(i,a,n) for(int i=n-1; i>=a; i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define fr(x) freopen(x,"r",stdin)
#define fw(x) freopen(x,"w",stdout)
typedef vector<int> VI;
typedef vector<char> VC;
typedef vector<VI> VVI;
typedef vector<VC> VVC;
typedef long long ll;
typedef pair<int, int> PII;
typedef unordered_map<int, int> MII;
typedef double db;
ll gcd(ll a, ll b){return b?gcd(a%b, b):a;}
const ll mod = 1000000007;
// head 
class Solution {
public:
    vector<bool> A;
    VVI B;
    int unhappyFriends(int n, vector<vector<int>>& P, vector<vector<int>>& pairs) {
        A.assign(n, true);
        B = P;
        int m = SZ(pairs);
        rep(i, 0, m) rep(j, i+1, m){
            int x = pairs[i][0];
            int y = pairs[i][1];
            int u = pairs[j][0];
            int v = pairs[j][1];
            f(x, y, u, v);
            f(y, x, u, v);
            f(x, y, v, u);
            f(y, x, v, u);
            f(u, v, x, y);
            f(u, v, y, x);
            f(v, u, x, y);
            f(v, u, y, x);
        }
            
        int ans = 0;
        for(auto a:A) if(!a) ans++;
        return ans;
    }
    void f(int x, int y, int u, int v){
       if(closer(x, u, y) && closer(u, x, v)) A[x] = false;
    }
    bool closer(int x, int u, int y){
        for(auto b: B[x]) if(b == u) return true; else if(b == y) return false;
        return false;
    }
};

P 连接所有点的最小费用

给你一个points 数组,表示 2D 平面上的一些点,其中 points[i] = [xi, yi] 。

连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 :|xi - xj| + |yi - yj| ,其中 |val| 表示 val 的绝对值。

请你返回将所有点连接的最小总费用。只有任意两点之间 有且仅有 一条简单路径时,才认为所有点都已连接。

并查集(TLE)

感觉没什么道理超时。
在这里插入图片描述

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define rep(i,a,n) for(int i=a; i<n; i++)
#define per(i,a,n) for(int i=n-1; i>=a; i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define fr(x) freopen(x,"r",stdin)
#define fw(x) freopen(x,"w",stdout)
typedef vector<int> VI;
typedef vector<char> VC;
typedef vector<VI> VVI;
typedef vector<VC> VVC;
typedef long long ll;
typedef pair<int, int> PII;
typedef unordered_map<int, int> MII;
typedef double db;
ll gcd(ll a, ll b){return b?gcd(a%b, b):a;}
const ll mod = 1000000007;
// head 
#include <vector>
#include <numeric>

using namespace std;

class UF {
public:
    vector<int> fa;
    vector<int> sz;
    int n;
    int comp_cnt;
    
public:
    UF(int _n): n(_n), comp_cnt(_n), fa(_n), sz(_n, 1) {
        iota(fa.begin(), fa.end(), 0);
    }
    
    int findset(int x) {
        return fa[x] == x ? x : fa[x] = findset(fa[x]);
    }
    
    void unite(int x, int y) {
        if (sz[x] < sz[y]) {
            swap(x, y);
        }
        fa[y] = x;
        sz[x] += sz[y];
        --comp_cnt;
    }
    
    bool findAndUnite(int x, int y) {
        int x0 = findset(x);
        int y0 = findset(y);
        if (x0 != y0) {
            unite(x0, y0);
            return true;
        }
        return false;
    }
};
struct E{int u, v, c;};
class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        int n = SZ(points), t;
        vector<E> e;
        rep(i, 0, n) rep(j, i+1, n){
            t = manhat_dist(points[i], points[j]);
            e.pb(E{i, j, t});
        } 
        sort(all(e), [](E&a, E&b){return a.c < b.c;});
        UF dsu(n);
        int ans = 0;
        for(auto x:e){
            if(dsu.findAndUnite(x.u, x.v)) ans += x.c;
            if(dsu.comp_cnt == 1) break;
        }
        return ans;
        

    }
    int manhat_dist(VI a,VI b){
        int x = abs(a[0] - b[0]);
        int y = abs(a[1] - b[1]);
        return x + y;
    }
};

P4 检查字符串是否可以通过排序子字符串得到另一个字符串

给你两个字符串 s 和 t ,请你通过若干次以下操作将字符串 s 转化成字符串 t :

选择 s 中一个 非空 子字符串并将它包含的字符就地 升序 排序。
比方说,对下划线所示的子字符串进行操作可以由 “14234” 得到 “12344” 。

如果可以将字符串 s 变成 t ,返回 true 。否则,返回 false 。

一个 子字符串 定义为一个字符串中连续的若干字符。

分析

  1. 每次交换操作: 选一个区间,然后排序, 那么得到的序列一直在变小。
  2. 那么是不是可以从高位开始,每一个数字都和target数字对齐, 如果不相等, 就选择最近的数字进行排序。
    e.x 434 -> 344 -> 找到第一个3, 然后做排序。
    那么这样做我们的方法是是固定的, 也就说按照这个策略我们可以模拟得到一个解。
    b{A}a = axxxxx <=> a{sorted([A + b]} = axxxxx
    b{Aa{B}a = axxx <=> aa(sorted([A + B + b)) = axxxx
    看着感觉找两个a,就不太行了, 因为这要求axxx里面第二位也得是a, 而且要保证A和B里面没有比a小的。
    所以, 我们只能找就近的相等数字进行操作。而且, 每次排完A这个序列也排完了不能懂了, 除非后面有更小的要上来。
    再看一下数据范围, 1e5, 字符集大小为10。 不是很大, 大概nlogn。
    先扫一遍, 记录
    A [ 0 ] = i 0 , 0 , i 0 , 1 , i 0 , 2 , . . . i 0 , k A[0] = {i_{0,0}, i_{0,1}, i_{0,2}, ... i_{0,k}} A[0]=i0,0,i0,1,i0,2,...i0,k
    A [ 9 ] = i 9 , 0 , i 9 , 1 , i 9 , 2 , . . . i 9 , k A[9] = {i_{9,0}, i_{9,1}, i_{9,2}, ... i_{9,k}} A[9]=i9,0,i9,1,i9,2,...i9,k
    感觉差不多, 如果我们要找的数字在上一位的操作中已经被用掉了, 那么它一定排在另外一个比它小的数字后, 也就是说当前的数字比target要小, 那么久凉了。 反之, 它还在原数组中, 那么,我们可以用预处理得到的个位数字索引。 然后,在设计一个指针指向将要使用下一位元素, 就行了。
    当然,还要考虑相等的情况, 如果相等, 俺么只需要移动该数字的指针指向下一位了。 如果越界了, 说面target中出现的数字比给的那个要多。 比如说 2211 => 1111。返回就好了。 看似很不错, 我们只需要O(n)的时间进行预处理, 然后开始遍历元字符串, 每个字符的操作只是选取一小段子字符串进行排序一定小log(n)。
    恩, 那一定AC了。

以上部分分析存在一个严重的失误点在于我们选取区间后如果直接对该区间进行排序,得到的序列并不是最大的。 就是说它会丢失掉一些可能出现的解。

示例 1:

输入:s = "84532", t = "34852"
输出:true
解释:你可以按以下操作将 s 转变为 t :
"84532" (从下标 2 到下标 3)-> "84352"
"84352" (从下标 0 到下标 2) -> "34852"

这个示例就说明,我刚才的方法是得不到true的, 因为第一次8和3对比交换排序之后得到的s=“34582”, 已经小于t了。
如果我们选择区间的时候每次选两个,那么s中其他的元素顺序会保持不变。 有了这个醒之后, 代码更简单了。

Hard题的标准可能是需要进行一系列推倒,然后得出一些结论在进行编程。

代码

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define rep(i,a,n) for(int i=a; i<n; i++)
#define per(i,a,n) for(int i=n-1; i>=a; i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define fr(x) freopen(x,"r",stdin)
#define fw(x) freopen(x,"w",stdout)
typedef vector<int> VI;
typedef vector<char> VC;
typedef vector<VI> VVI;
typedef vector<VC> VVC;
typedef long long ll;
typedef pair<int, int> PII;
typedef unordered_map<int, int> MII;
typedef double db;
ll gcd(ll a, ll b){return b?gcd(a%b, b):a;}
const ll mod = 1000000007;
// head 
class Solution {
public:
    bool isTransformable(string s, string t) {
        int n = SZ(s), m = SZ(t), d;
        if(n != m ) return false;
        int pos[10][n];
        rep(i, 0, 10) rep(j, 0, n) pos[i][j] = -1;
        int ptr[10];
        rep(i, 0, 10) ptr[i] = 0;
        rep(i, 0, n) {
            d = s[i] - '0';
            pos[d][ptr[d]] = i; 
            ptr[d]++;
        }
        
        rep(i, 0, 10) ptr[i] = 0;
        int i = 0;
        vector<bool> vis;
        vis.assign(n, false);
        rep(j, 0, n){
            while(vis[i]) i++; 
            if(s[i] == t[j]) {i ++; continue;}
            if(s[i] < t[j]) return false;
            d = t[j] - '0';
            if(pos[d][ptr[d]] == -1) return false;  
            vis[pos[d][ptr[d]]] = true;
            ptr[d] ++;
        }
        return true;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值