码迷,mamicode.com
首页 > 其他好文 > 详细

18.8.26 考试总结

时间:2018-08-26 22:04:10      阅读:141      评论:0      收藏:0      [点我收藏+]

标签:include   ||   define   printf   min   联通   code   can   struct   

技术分享图片

我真的服了 我考试的时候这道题题都是读错了的 交了个挖挖机结果还狗了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\n",(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\n");
        else printf("%d\n",nd.tr[0][4]);
    }
}

18.8.26 考试总结

标签:include   ||   define   printf   min   联通   code   can   struct   

原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9538923.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!