力扣301周赛C~D&ABC299 D、E、G

第 301 场周赛

C:力扣301周赛C~D&ABC299 D、E、G_第1张图片

 

思路:

经典双指针问题,用i表示字符串start当前枚举到的位置,用j表示字符串target当前枚举到的位置:

  1. i从当前位置向后跑,跑到start串下标i之后的第一个不为'_'的位置

  2. j从当前位置向后跑,跑到target串下标j之后的第一个不为'_'的位置

  3. 判断start[i]与target[j]是否相同,如果不同,指定是无解了。

  4. 如果当前start[i]=='L'时,应该是i>=j,如果是'R'则应该是i<=j,否则无解。

  5. 检查i

  6. 如果i

    代码:

    class Solution:
        def canChange(self, start: str, target: str) -> bool:
            n=len(start)
            i=j=0
            while ij:
                        return False
                    i+=1
                    j+=1
            while i

D:力扣301周赛C~D&ABC299 D、E、G_第2张图片

 

思路:

不会,参考题解

数学 & 递推,附复杂度与 n 无关(klogk)的做法:

2338. 统计理想数组的数目 - 力扣(LeetCode)

代码

class Solution {
    const int MOD=1e9+7;
    const int MAXP=16;
​
public:
​
    int idealArrays(int n, int K) {
        //nlnn求因数
        vector>fac(K+1);
        for(int i=1;i<=K;i++){
            for(int j=i<<1;j<=K;j+=i){
                fac[j].push_back(i);
            }
        }
​
        //计算子问题答案,f[i][j]表示以i为结束元素,长度为j的方案个数,长度为j的序列元素两两不同
        vector>f;
        f.resize(K+1,vector(20));
        for(int i=1;i<=K;i++){
            f[i][1]=1;
            for(int j=2;j<=MAXP;j++){
                for(int t:fac[i]){
                    f[i][j]=(f[i][j]+f[t][j-1])%MOD;
                }
            }
        }    
​
        //求组合数
        vector>C;
        C.resize(n+1,vector(20));
​
        C[0][0]=C[1][0]=C[1][1]=1;
        for(int i=2;i<=n;i++){
            C[i][0]=1;
            for(int j=1;j<=i&&j<=MAXP;j++){
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
            }
        }
​
        //统计最终答案
​
        long long ans=0;
        for(int i=1;i<=K;i++){//终点
            for(int j=1;j<=MAXP;j++){//选几个数
                ans=(ans+C[n-1][j-1]*f[i][j])%MOD;
                //最终元素为i,从n个元素里面选取长度为j的不重复元素,将这j个不重复元素确定好位置,这个长度为n
                //的序列就确定了,那么第一个最小的元素肯定在位置1,那剩下来的n-1个位置,放置j-1个数就行了
                //因为最终元素为i,长度为j的序列中元素两两不同的方案书为f[i][j],选取的位置有C[n-1][j-1]
                //所以乘法原理即可
            }
        }
        return ans;
    }
};

ABC299

D - Find by Query

题目大意:

力扣301周赛C~D&ABC299 D、E、G_第3张图片

力扣301周赛C~D&ABC299 D、E、G_第4张图片 

 

思路:

看到最多询问20次,长度为2e5,首先想到的应该是二分,怎么二分呢?

注意到S[1]='0',S[N]='1',那么每次查询中间的位置,如果query(l+r>>1)=='0',那就令l=l+r>>1,否则r=l+r>>1

,这样就能时刻保证S[l]='0',S[r]='1'

代码:

#include 
​
using namespace std;
​
int main()
{
    int n;
    cin >> n;
    int l = 1, r = n;
    int cnt = 20;
    while (cnt > 0)
    {
        cnt--;
        int mid = l + r >> 1;
        cout << "? " << mid << endl;
        char x;
        cin >> x;
        if (l + 1 == mid && x == '1')
        {
            cout << "! " << l << endl;
            return 0;
        }
        if (mid + 1 == r && x == '0')
        {
            cout << "! " << mid << endl;
            return 0;
        }
        if (x == '0')
        {
            l = mid;
        }
        else
        {
            r = mid;
        }
    }
    return 0;
}

E - Nearest Black Vertex

题目大意:

力扣301周赛C~D&ABC299 D、E、G_第5张图片 

思路:

  1. 首先建图,然后跑最短路,堆优化迪杰斯特拉的时间复杂度为O(M*log(N)),但是这个题边权全为1,就不需要堆优化了,普通队列即可,所以求以每一个点s(1<=s<=n)为起点到其他点的最短路径的时间复杂度为O(NM)

  2. 读入k个限制,u,d,遍历以u为起点,到其他点(下面用v表示)的最短路径,如果

    dist[ u ][ v ] < d

    ,则这个点不可能是黑色点,标记

  3. 全部标记完之后,再次遍历k个限制,遍历每个限制是否有解即可

    即寻找dist[u][v]==d,是否有点满足,并且这个点v没有被标记为不能做为黑色点

    代码:代码写的与思路有一点不一样,我的代码在读入限制的时候就判断他有没有可行点解了,就是

dist[u][v]==d是否存在,如果已经找到一个点不满足就不接着做了,直接输出No即可,现在看来有点鸡肋了,即使这样做,等k个限制跑完之后,仍然需要再次跑k个限制看看是否满足条件,因为假如你
u1,找到某个唯一可以当作黑色点的v1,此时这个v1还没有被标记为不能当作黑点
接下来限制u2 d的时候,把v1标记做了不能当作黑点,只遍历一遍是不行的也就是说如果说只有这两个限制,只跑一边会输出Yes,然后输出可行解,但实际是No,因为u1的唯一可以当作黑点的v1被u2 hcak掉了
#include 
using namespace std;
​
#define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define PII pair
#define de(a) cout << #a << " = " << a << "\n";
#define deg(a) cout << #a << " = " << a << " ";
#define endl "\n"
#define int long long
#define LL long long
const int mod = 1e9 + 7;
const int N = 1e6 + 5;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
​
vector edge[N];
int dist[2005][2005];
int n, m;
void dijkstra(int s)
{
    vector vis(n + 1, 0);
    dist[s][s] = 0;
    queue q;
    q.push(s);
    while (q.size())
    {
        int t = q.front();
        q.pop();
        if (vis[t])
            continue;
        vis[t] = true;
        for (auto v : edge[t])
        {
            if (dist[s][v] > dist[s][t] + 1)
            {
                dist[s][v] = dist[s][t] + 1;
                q.push(v);
            }
        }
    }
}
​
void solve()
{
​
    cin >> n >> m;
    for (int i = 0; i <= n + 1; i++)
    {
        for (int j = 0; j <= n + 1; j++)
        {
            dist[i][j] = INT64_MAX;
        }
    }
    for (int i = 0; i < m; i++)
    {
        int u, v;
        cin >> u >> v;
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    for (int i = 1; i <= n; i++)
    {
        dijkstra(i);
    }
    int k;
    vector st(n + 1, 1);
    cin >> k;
    vector query;
    bool ok = true;
    for (int i = 0; i < k; i++)
    {
        int u, d;
        cin >> u >> d;
        query.emplace_back(u, d);
        bool f = 0;
        for (int v = 1; v <= n; v++)
        {
            if (dist[u][v] < d)
            {
                st[v] = 0; // 不能为黑色
            }
            else if (dist[u][v] == d && st[v] == 1) // 找到合法黑色点了
            {
                f = 1;
            }
        }
        if (!f) // 这个点u没有合法点当u
            ok = 0;
    }
​
    for (int i = 0; i < k; i++)
    {
        auto [u, d] = query.back();
        query.pop_back();
        bool f = 0;
        for (int v = 1; v <= n; v++)
        {
            if (dist[u][v] < d)
            {
                st[v] = 0; // 不能为黑色
            }
            else if (dist[u][v] == d && st[v] == 1) // 找到合法黑色点了
            {
                f = 1;
            }
        }
        if (!f) // 这个点u没有合法点当u
            ok = 0;
    }
​
    if (ok)
    {
        cout << "Yes\n";
        for (int i = 1; i <= n; i++)
        {
            cout << st[i];
        }
    }
    else
    {
        cout << "No\n";
    }
}
​
signed main()
{
    FAST;
    int t = 1;
    // cin >> t;
    while (t--)
        solve();
​
    return 0;
}

G - Minimum Permutation

题目:

有一个长度为 N 的序列,保证其中包含1~M,请你找到一个长M的子序列,使得其中每个数字都出现了一次,并使得排列的字典序最小。

思路:

贪心。考虑单调栈(定义s[i]表示栈中第i个元素是什么,tt表示现在的栈顶元素所在的位置),顺序枚举这个长度为N的序列。

假如我们现在处理第i个元素,那么前i-1个元素已经处理好了,对于第i个元素a[i],

  • 如果a[i]在栈里,直接跳过就行了

  • a[i]>s[tt](栈顶),直接加到栈里面就可以了

  • a[i]因为我们去掉栈顶的条件是栈顶小于当前元素(保证字典序更小),后面出现过当前栈顶(保证后面可以把这个删去的栈顶加进来)

    #include 
    using namespace std;
    ​
    #define FAST ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
    #define PII pair
    #define de(a) cout << #a << " = " << a << "\n";
    #define deg(a) cout << #a << " = " << a << " ";
    #define endl "\n"
    #define int long long
    #define LL long long
    const int mod = 1e9 + 7;
    const int N = 1e6 + 5;
    int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
    ​
    int n, m, a[N], s[N], tt, d[N];
    bool st[N];
    ​
    void solve()
    {
        int n, m;
        cin >> n >> m;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
            d[a[i]]++;
        }
        for (int i = 1; i <= n; i++)
        {
            d[a[i]]--; // 这个数字出现过了一次
            if (st[a[i]])
                continue;
            while (s[tt] > a[i] && d[s[tt]]) // 栈顶大于当前元素,并且栈顶的元素在后面还有
                st[s[tt]] = 0, tt--;
            s[++tt] = a[i];
            st[a[i]] = 1;
        }
        for (int i = 1; i <= m; i++)
        {
            cout << s[i] << ' ';
        }
    }
    ​
    signed main()
    {
        FAST;
        int t = 1;
        // cin >> t;
        while (t--)
            solve();
    ​
        return 0;
    }

你可能感兴趣的:(算法)