zoukankan      html  css  js  c++  java
  • 18.8.26 考试总结

    我真的服了 我考试的时候这道题题都是读错了的 交了个挖挖机结果还狗了20分..

    这道题是一道找规律的题 看完题很显然能够发现我们可以将相同颜色的连通块缩点

    因为同一个联通块的可以一次操作全部变成另外一种颜色 所以就缩点就好了..

    对于缩点后的一条链

    每次我们可以将一个点变色 那么和他相邻的点就和他颜色一样 然后就再次缩点 所以每次链的长度都可以 -2

    所以说最后缩点的次数就是  点的个数 / 2  (下取整)

    但是这是对于一条链 那么对于一棵树而言呢?

    其实是一样的 因为每次搞这个操作 我们都可以把它周围的点都缩起来 那么对于多条链的情况

    每次就选择两链的交点进行这个操作 经过这个点的所有链长度均 - 2  所以操作次数就是最长链 也就是直径的点数 / 2(下取整)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e5 + 5;
    int n,c[N],node,T,tot,head[N],nex[2 * N],tov[2 * N];
    int id[N],fa[N],dis[N],ma,ev[N],eu[N],cnt;
    bool vis[N];
    queue<int>Q;
    
    int find_fa(int u) {
        
        return u == fa[u] ? u : find_fa(fa[u]);
    }
    
    void merge(int u,int v) {
        
        int fa1 = find_fa(u);
        int fa2 = find_fa(v);
        if(fa1 == fa2) return ;
        fa[fa2] = fa1;
    }
    
    void add(int u,int v) {
        
        tot ++;
        nex[tot] = head[u];
        tov[tot] = v;
        head[u] = tot;
    }
    
    void bfs(int st) {
        
        memset(dis,0,sizeof(dis));
        memset(vis,0,sizeof(vis));
        dis[st] = 0; vis[st] = true;
        node = st,ma = 0;
        Q.push(st);
        while(! Q.empty( )) {
            int u = Q.front( ); Q.pop( );
            for(int i = head[u];i;i = nex[i]) {
                int v = tov[i];
                if(vis[v]) continue;
                vis[v] = true;
                dis[v] = dis[u] + 1;
                if(dis[v] > ma) {
                    ma = dis[v];
                    node = v;
                }
                Q.push(v); 
            }
        }
        return ;
    }
    
    void init( ) {
        
        memset(head,0,sizeof(head));
        tot = 0;
        for(int i = 1;i <= n;i ++) fa[i] = i;
    }
    
    int main( ) {
        
        freopen("color.in","r",stdin);
        freopen("color.out","w",stdout);
        scanf("%d",& T);
        while(T --) {
            scanf("%d",& n);
            init( );
            for(int i = 1;i <= n;i ++) scanf("%d",& c[i]);
            for(int i = 1;i < n;i ++) {
                scanf("%d%d",& eu[i],& ev[i]);
                if(c[eu[i]] == c[ev[i]]) {
                    merge(eu[i],ev[i]);
                }
            }
            for(int i = 1;i < n;i ++) {
                int f1 = find_fa(eu[i]),f2 = find_fa(ev[i]);
                if(f1 != f2) {
                    if(! id[f1]) {
                        id[f1] = ++ cnt;
                    }
                    if(! id[f2]) {
                        id[f2] = ++ cnt;
                    }
                    add(id[f1],id[f2]);
                    add(id[f2],id[f1]);
                }
            }
            bfs(1);
            bfs(node);
            printf("%d
    ",(dis[node] + 1)/2);
        }
    }

    考试的时候打得垃圾暴力狗了30

    这道题是一道bfs模拟 题 因为t很小 所以可以发现从初始点(175,175)出发的点范围肯定在(400,400)以内

    每一个状态可以拓展出的状态很多 所以会出现很多重复的状态 拓展过的状态就不用重新再计算了 

    所以就开一个vis数组表示这个情况是否被拓展过 然后就很简单了

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e5 + 5;
    int dp[400][400],t[N],h[2] = {-1,1},n,ans = 0;
    bool vis[400][400][40][8];
    struct node {
        
        int x,y,s,d;
        node(){}
        node( int x, int y, int s, int d ):x(x),y(y),s(s),d(d){}
    }stat;
    int dx[] = {0,-1,-1,-1,0,1,1,1};
    int dy[] = {1,1,0,-1,-1,-1,0,1};
    queue<node>Q;
    
    void bfs( ) {
        
        while(! Q.empty()) {
            node u = Q.front( );
            Q.pop( );
            int xx = u.x,yy = u.y,dep = u.s;
            if(dep == n) continue;
            for(int i = 0;i <= 1;i ++) {
                int dd = ((u.d + h[i]) % 8 + 8) % 8;
                for(int j = 1;j <= t[dep + 1];j ++) {
                    xx += dx[dd]; yy += dy[dd];
                    dp[xx][yy] = 1;
                }
                if(! vis[xx][yy][dep + 1][dd]) {
                    vis[xx][yy][dep + 1][dd] = true;
                    Q.push(node(xx,yy,dep + 1,dd));
                }
                xx = u.x,yy = u.y;
            }
        }
        for(int i = 0;i < 400;i ++)
          for(int j = 0;j < 400;j ++)
            ans += dp[i][j];
    }
    
    int main( ) {
        
        freopen("grow.in","r",stdin);
        freopen("grow.out","w",stdout);
        scanf("%d",& n);
        for(int i = 1;i <= n;i ++) scanf("%d",& t[i]);
        for(int i = 1;i <= t[1];i ++) {
            dp[175][174 + i] = true;
        }
        vis[175][174 + t[1]][1][0] = true;
        Q.push(node(175,174 + t[1],1,0));
        bfs( );
        printf("%d",ans);
    }

    这道题可以转化为类似于状态合并的问题   共有5个状态  空集 2 20 201 2017

    tr[ i ][ j ]表示从 i 号状态转移到 j 号状态 最少需要删掉多少字符

    那么类似于Floyd 对于两个区间 a b及他的左右两个区间

    tr[ i ][ j ] = min(tr[ i ][ k ] + tr[ k ][ j ]) 总的状态可以由他的两个连续子状态转移过来

    但是每次都去暴力搞就很糟糕 所以考虑用线段树维护就可以了

    代码

    #include <bits/stdc++.h>
    #define oo 0x3f
    using namespace std;
    
    const int N = 2e5 + 5;
    int n,a[N],q;
    char s[N];
    struct Info {
        
        int tr[5][5];
        void init(int cur) {
            memset(tr,0x3f,sizeof(tr));
            if(cur == 3 || cur == 4 || cur == 5 || cur == 8 || cur == 9) {
                for(int i = 0;i <= 4;i ++) tr[i][i] = 0;
            }
            if(cur == 2) {
                tr[0][0] = 1;
                tr[0][1] = 0;
                tr[1][1] = tr[2][2] = tr[3][3] = tr[4][4] = 0;
            }
            else if(cur == 0) {
                tr[1][2] = 0;
                tr[1][1] = 1;
                tr[0][0] = tr[2][2] = tr[3][3] = tr[4][4] = 0;
            }
            else if(cur == 1) {
                tr[2][3] = 0;
                tr[2][2] = 1;
                tr[0][0] = tr[1][1] = tr[3][3] = tr[4][4] = 0;
            }
            else if(cur == 6) {
                tr[3][3] = 1;
                tr[4][4] = 1;
                tr[0][0] = tr[1][1] = tr[2][2] = 0;
            }
            else if(cur == 7) {
                tr[3][4] = 0;
                tr[3][3] = 1;
                tr[0][0] = tr[1][1] = tr[2][2] = tr[4][4] = 0;
            }
        }
    };
    
    Info operator + (const Info & a,const Info & b) {
        
        Info rt;
        memset(& rt,0x3f,sizeof(rt));
        for(int i = 0;i <= 4;i ++)
          for(int j = 0;j <= 4;j ++)
            for(int k = i;k <= j;k ++)
              rt.tr[i][j] = min(rt.tr[i][j],a.tr[i][k] + b.tr[k][j]);
        return rt;
    }
    
    struct node {
        
        Info info;
        node *ls,*rs;
    }pool[4 * N],*tail = pool,*root;
    
    node *build(int l,int r) {
        
        node *nd = ++ tail;
        if(l == r) {
            nd -> info.init(a[l]);
            return nd;
        }
        int mid = (l + r) >> 1;
        nd -> ls = build(l,mid);
        nd -> rs = build(mid + 1,r);
        nd -> info = nd -> ls -> info + nd -> rs -> info;
        return nd;
    }
    
    Info query(node *nd,int l,int r,int L,int R) {
        
        if(l >= L && r <= R) {
            return nd -> info;
        }
        int mid = (l + r) >> 1;
        if(R <= mid) return query(nd -> ls,l,mid,L,R);
        else if(L > mid) return query(nd -> rs,mid + 1,r,L,R);
        else return query(nd -> ls,l,mid,L,R) + query(nd -> rs,mid + 1,r,L,R);
    }
    
    int main( ) {
        
        freopen("year.in","r",stdin);
        freopen("year.out","w",stdout);
        scanf("%s",s + 1);
        int len = strlen(s + 1);
        for(int i = 1;i <= len;i ++)
          a[i] = s[i] - '0';
        root = build(1,len);
        scanf("%d",& q);
        while(q --) {
            int l,r;
            scanf("%d%d",& l,& r);
            Info nd = query(root,1,len,l,r);
            if(nd.tr[0][4] == 1061109567) printf("-1
    ");
            else printf("%d
    ",nd.tr[0][4]);
        }
    }
  • 相关阅读:
    hdoj 1002 A + B Problem II
    hdoj 1234 开门人和关门人
    hdoj 2203 亲和串
    nyoj 73 比大小
    81B
    信息传递
    bzoj1787
    最少交换次数
    100803C
    火柴排队
  • 原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9538923.html
Copyright © 2011-2022 走看看