zoukankan      html  css  js  c++  java
  • 最近算法的practice

    2020/7/2 要点
    普通dp 两个字符串的匹配,一个字符串可以添删改其中的字符,求变成另一个字符串的最少操作步数,dp[i][j]表示分别到i和j位置的最少步数,这样状态无后效性
    模拟题 字符串模拟,用python方便很多, ord和chr两个函数,利用 map(int,input().strip().split())输入,
    水题 字符串模拟,python:a[::-1]实现字符串reverse
    2020/7/3 要点
    水题 对于int,ceil(n/2.0)和(n+1)/2 不一样?不一样,前者输出时doubel,后者时int,例如对于n=493796142,2.46898e+008和246898071
    水题 看清题意即为等差数列+特判
    水题

    时间好快呀,一晃过去六天没写题

    2020/7/9 要点
    模拟题 利用递归或者栈做字符串解压模拟,从外到里,一层层去掉括号,可以保证顺序不变
    贪心 开始以为是个dp,发现复杂度过不去。注意枚举顺序,利用有限队列维护。priority_queue<ll,vector<ll>,greater<ll> >是小顶堆
    区间dp 1.区间dp的特点是,区间长度较小,而且一般状态三四个维度,最外层枚举区间长度,第二层确定起点
    区间dp 求最长回文子序列,(dp[i][j]) 为字符串s 的第 i 个字符到第 j个字符的最长回文子序列长度。状态转移:(if: s[i]==s[j],dp[i][j]=dp[i+1][j-1]+2. else:dp[i][j]=max(dp[i+1][j],max[i][j-1])
    区间dp 求最长回文子串,我们可以强行规定(dp[i][j]) s中第 i 个字符和第 j 个字符都必须选,换句话说 (dp[i][j]) 其实是选完了整个 i 到 j 的区间的,也就是说,(dp[i][j]==j-i+1)时,这个区间是个回文串,否则就不是,(dp[i][j]=0) ,所以只需要用0,1表示是否是回文串。状态转移:(if :dp[i+1][j-1]==1 ,and ,s[i]==s[j]时,dp[i][j]=1,else :dp[i][j]=0)
    区间dp 利用两个字符串,组拼成为一个字符串,只需要保持原来每个字符串的字符相对位置不变即可,与上一个类似,(dp[i][j][k][l])表示选择第一个字符串i到j的位置,第二个字符串k到l的位置,是否组成回文串,四个状态注意。

    2020/7/14

    题意:将n个石子分成k份,且每份不能为空,任意两个方案不能相同(不考虑顺序)。
    把n划分为k个,那么先从n中拿一个x,题目便变为将(n-x)分为k-1种,所以我们可以依次拿一个1出来,递推式为(f[i−1][j−1]),即有1的情况;

    没有1的情况,可以看成为j个元素预分配一个1,剩下的便没有1,即f[i-j][j]f[i−j][j]。故当i>ji>j时便有了递推式(f[i][j]=f[i-1][j-1]+f[i-j][j])
    超时dfs,保存每一种方案

    void dfs(int num,int cur,int big){
        if(cur==1){
            v.push_back(num);
            v_copy.assign(v.begin(),v.end());
            sort(v_copy.begin(),v_copy.end());
            se.insert(v_copy);
             v.pop_back();
            return;
        }
        for(int i=big;i<=num-cur+1;i++){
            v.push_back(i);
            dfs(num-i,cur-1,big);
            v.pop_back();
        }
    }
    

    不超时dfs:

    int dfs(int num,int cur){
         if(num<cur)return 0;
         if(cur==1)return 1;
         return dfs(num-1,cur-1)+dfs(num-cur,cur);
        
    }
    

    2020/7/19

    题意:判断一个串是否是另一个串的子序列。(都只包含小写字母)

    题解:如果主串中有两个一样的字母,我们肯定会选前一个,因为是子序列,如果选前一个不可以,那么选后面的同一个字母肯定就更不可以了。
    开一个数组(next[i][′a′...′z′])表示主串第 i个字母之后(包含第i个)的第一个′a′...′z′分别在哪一个位置,子串每次与之匹配的时候顺着往后走就行。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+10;
    char str[N];
    char s[N];
    int l;
    int ind[N][26];//包括i后面的位置的第一个j出现的位置
    void init(){
        l=strlen(str+1);
        for(int j=0;j<26;j++){
            int last=-1;
        for(int i=l;i>=0;i--){
            if(str[i]-'a'==j)last=i;
            ind[i][j]=last;
        }
        }
    }
    int main(){
        scanf("%s",str+1);
        int n;
        scanf("%d",&n);
        init();
        
        while(n--){
            bool f=true;
            scanf("%s",s+1);
            int len=strlen(s+1);
            int cur=0;
            for(int i=1;i<=len;i++){
                if(cur>l){
                    f=false;
                    break;
                }
                cur=ind[cur][s[i]-'a'];
                if(cur==-1){
                    f=false;
                    break;
                }
                cur++;
            }
            if(f)puts("Yes");
            else puts("No");
        }
        return 0;
    }
    

    题意:给你一棵 n 个节点的树(保证 n 是偶数),你需要将 n 个节点分为 n/2 个点对,使得每个点对的两个点的距离的和最小。
    题解:

    • 首先,在最短的距离和之中一条边一定不会被覆盖两次。
    • 对于一个点x,它子树中的点一定会尽量在子树中找到匹配的点内部消化掉(要么连父亲要么连兄弟),只有根是有可能会往上找一个点来匹配(不然又会出现重复覆盖一条边的情况。
    • 那么如果我们不去想点怎么两两配对而是来考虑每个边选不选——对于当前点 x,如果它的子树大小(包括它自己)有偶数个点,那么肯定在子树里面就互相连完了,它不需要向上连;如果是奇数个点,x 就需要去匹配上面的点了,所以 x 向它父亲连的边就要选。
      代码:
    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    struct edge{
        int from,to,cost;
        edge(int _from,int _to,int _cost){
            from=_from;to=_to;cost=_cost;
        }
    };
    const int N=1e4+10;
    vector<edge>ve;
    vector<int>G[N];
    int tot[N];//tot[i]表示包含i点的i的子树大小
    void add_edge(int u,int v,int cost){
        ve.push_back(edge(u,v,cost));
        G[u].push_back(ve.size()-1);
    }
    ll ans=0;
    void dfs(int u,int fa,int len){
        tot[u]=1;
        for(int i=0;i<G[u].size();i++){
            int id=G[u][i];
            int v=ve[id].to;
            if(v==fa)continue;
            dfs(v,u,ve[id].cost);
            tot[u]+=tot[v];
        }
        if(tot[u]%2==1)ans+=len;
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            int n;
            ans=0;
            scanf("%d",&n);
            for(int i=0;i<n-1;i++){
                int u,v,w;
                scanf("%d%d%d",&u,&v,&w);
                add_edge(u,  v, w);
                add_edge(v,  u, w);
            }
            dfs(1,0,0);
            printf("%lld
    ",ans);
            for(int i=0;i<=n;i++)G[i].clear();
            ve.clear();
        }
        return 0;
    }
    
  • 相关阅读:
    Linux上安装Tomcat
    SQLServer2008 关于while循环
    [转]接口和抽象类
    windows 装XP系统
    SQLServer2008 表与表之间的数据导入
    问题消灭机
    报错。。。。。。。。。。
    疑问...........
    SQLServer In和Exists
    struts2 访问一个action的时候出现多次重复访问问题(2次或者3次)
  • 原文地址:https://www.cnblogs.com/gzr2018/p/13302336.html
Copyright © 2011-2022 走看看