zoukankan      html  css  js  c++  java
  • 2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017) D bfs思维 E bfs I Floyd找最小环 K 二分

    2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)

    题意:有 n 个人,每个人有 k 个特征,每个特征值为 0或 1。 定义两人的相似度为:k 个特征值中相同的个数。 要你另外找一个人,这个人和其他人相似度的最大值要尽可能小。

    tags:bfs,思维

    找一个人与其他人相似度的最大值尽可能小,也就是这个人与其他人 不相似度的最小值要尽可能大。

    那我们可把这 n 个人当成 n 个点,总共有 (1<<k) 个点。我们以这 n 个点为起点 bfs,每走一步改变一个特征值,即 不相似度 +1 。最后找 不相似度最大的就是了。

    //  D
    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    const int N = 2000005;
    
    int n, k, dis[N];
    char str[N];
    queue< int > q;
    void bfs()
    {
        int u, to;
        while(!q.empty())
        {
            u = q.front();  q.pop();
            rep(j,1,k)
            {
                to = u^(1<<(j-1));
                if(dis[to]==-1) {
                    dis[to] = dis[u]+1;
                    q.push(to);
                }
            }
        }
    }
    int main()
    {
        scanf("%d%d", &n, &k);
        mes(dis, -1);
        rep(i,1,n)
        {
            scanf("%s", str+1);
            int tmp = 0;
            rep(j,1,k)
                if(str[j]=='1') tmp+=1<<(j-1);
            dis[tmp]=0;   q.push(tmp);
        }
        bfs();
        int ans1, ans2=0;
        rep(i,0,(1<<k)-1)
            if(dis[i]>=ans2) ans1=i, ans2=dis[i];
        rep(j,1,k)
            if((ans1>>(j-1))&1) putchar('1');
            else putchar('0');
    
        return 0;
    }
    View Code

    E

    题意:n*m 的地方,每个地方给出高度,所有 n*m 的地方有海拔为 0 的积水。 现在给出一个点作为排水口,每个地方的水可以往周围八个方向,且比它低的地方流。问最后会流走多少水。

    tags:bfs 跑一遍,同时要记录每个点可以下降到的最低点,且优生走更低的点。

    //  E
    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    const int N = 505;
    
    int n, m, a[N][N], ans[N][N];
    struct Node {
        int x, y, h;
        bool friend operator<(Node a, Node b) {
            return a.h > b.h;
        }
    };
    int dirx[8] = {1, -1, 0, 0, 1, 1, -1, -1};
    int diry[8] = {0, 0, 1, -1, 1, -1, -1, 1};
    priority_queue< Node > q;
    bool check(int x, int y) {
        return x>0 && x<=n && y>0 && y<=m;
    }
    void bfs(int x, int y, int h)
    {
        Node u;
        q.push((Node){ x,y,h });
        int tx, ty;
        while(!q.empty())
        {
            u = q.top();  q.pop();
            x=u.x, y=u.y, h=u.h;
            if(ans[x][y]<h) continue;
            rep(i,0,7)
            {
                tx=x+dirx[i],  ty=y+diry[i];
                if(check(tx,ty) && max(a[tx][ty],h)<0 && ans[tx][ty]>max(a[tx][ty],h))
                {
                    ans[tx][ty] = max(h, a[tx][ty]);
                    q.push((Node){ tx,ty,ans[tx][ty] });
                }
            }
        }
    }
    int main()
    {
        scanf("%d%d", &n, &m);
        rep(i,1,n) rep(j,1,m)
            scanf("%d", &a[i][j]);
        int tx, ty;
        scanf("%d%d", &tx, &ty);
        ans[tx][ty]=min(0, a[tx][ty]);
        bfs(tx, ty, a[tx][ty]);
        ll  ans1 = 0;
        rep(i,1,n) rep(j,1,m)
            if(ans[i][j]<0)
                ans1 += -ans[i][j];
        printf("%lld
    ", ans1);
    
        return 0;
    }
    View Code

    题意: n 个文件,每个文件可以调用多个其它的文件,问最小的循环调用,且输出路径。 也就是在 DAG 图中找一个最小环。

    tags:套板子-_-

    Floyd 有向图找最小环

    // Floyd 找有向图最小环
    const int N = 505;
    int G[N][N], n ,m, dist[N][N], past[N][N];
    int mincircle, path[N], cnt;
    void Init_Floyd()
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                G[i][j] = INF;
    }
    void Floyd()
    {
        mincircle = INF;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                dist[i][j] = G[i][j], past[i][j] = i;
    
        for(int k = 1; k <= n; k++)  //每个点都成为一次中间点,但和bellman-ford不一样
        {
            for(int i = 1; i <= n; i++) //判断是不是最小环
                for(int j = 1; j <= n; j++)
                {
                    if(i == j)  continue;
                    if(dist[i][j]!=INF && G[j][k]!=INF && G[k][i]!=INF && mincircle>dist[i][j]+G[j][k]+G[k][i])
                    {
                        mincircle = dist[i][j]+G[j][k]+G[k][i];
                        cnt = 0;
                        int  p=j;
                        while(p!=i) //逆向寻找前驱结点直到找到最前面的i,i->…->j
                        {
                            path[cnt++]=p;
                            p=past[i][p];
                        }
                        path[cnt++]=i;
                        path[cnt++]=k;
                    }
                }
    
            for(int i = 1; i <= n; i++) // 这里就是 Floyd算法
                for(int j = 1; j <= n; j++)
                {
                    if(i == k || j == k)  continue;
                    if(dist[i][k]!=INF && dist[k][j]!=INF && dist[i][j]>dist[i][k]+dist[k][j])
                    {
                        dist[i][j] = dist[i][k]+dist[k][j];
                        past[i][j] = past[k][j];
                    }
                }
        }
    }
    void print_path()
    {
        if(mincircle==INF || mincircle<0)
            puts("-1");
        else {
            for(int i=cnt-1; i>=0; --i)
                printf("%d ", path[i]);
            puts("");
        }
    }
    //   I
    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    const int N = 505;
    
    int grap[N][N] , n , m;
    int dist[N][N];
    int past[N][N];
    int mincircle;
    int path[N] , k1 ;
    void Init_Floyd()
    {
        int i , j;
        for(i = 1; i <= n; i++)
            for(j = 1; j <= n; j++)
                grap[i][j] = INF;
    }
    void Floyd()
    {
        mincircle = INF;
        int i , j;
        for(i = 1; i <= n; i++)
            for(j = 1; j <= n; j++)
            {
                dist[i][j] = grap[i][j];
                past[i][j] = i;
            }
    
        for(int k = 1; k <= n; k++)  //每个点都成为一次中间点 , 和bellman-ford不一样
        {
            for(i = 1; i <= n; i++) //判断是不是最小环
                for(j = 1; j <= n; j++)
                {
                    //if(i == j)  continue;
                    if(dist[i][j]!=INF && grap[j][k]!=INF && grap[k][i]!=INF && mincircle>dist[i][j]+grap[j][k]+grap[k][i])
                    {
                        mincircle = dist[i][j]+grap[j][k]+grap[k][i];
                        k1 = 0;
                        int  p=j;
                          while(p!=i) //逆向寻找前驱结点直到找到最前面的i,i->…->j
                          {
                                path[k1++]=p;
                                p=past[i][p];//fa[i][j]保存的不是k,而是fa[k][j].
                          }
                          path[k1++]=i;
                          path[k1++]=k;
                    }
                }
    
            for(i = 1; i <= n; i++) //Floyd算法
                for(j = 1; j <= n; j++)
                {
                    //if(i == k || j == k)  continue;
                    if(dist[i][k]!=INF && dist[k][j]!=INF && dist[i][j]>dist[i][k]+dist[k][j])
                    {
                        dist[i][j] = dist[i][k]+dist[k][j];
                        past[i][j] = past[k][j];
                    }
                }
        }
    }
    
    string str;
    char si[N];
    map<string , int > mp;
    map<int , string > mp2;
    int main()
    {
        scanf("%d", &n);
        Init_Floyd();
        rep(i,1,n) {
            scanf("%s", si);   str = si;
            mp[str]=i,  mp2[i]=str;
        }
        int k, u, len, to;
        bool flag;
        rep(i,1,n)
        {
            scanf("%s%d", si, &k);  str = si;
            u = mp[str];
            rep(j,1,k)
            {
                scanf("%s", si);
                while(~scanf("%s", si))
                {
                    flag = false;
                    len = strlen(si);
                    if(si[len-1]==',') {
                        flag=true;
                        si[len-1]='',  --len;
                    }
                    str = si;
                    to = mp[str];
                    if(grap[u][to]>1)
                        grap[u][to]=1;
                    if(!flag) break;
                }
            }
        }
        rep(i,1,n) if(grap[i][i]<INF) {
            cout<<mp2[i]<<endl;
            return 0;
        }
        rep(i,1,n) rep(j,1,n)
        {
            if(i!=j && grap[i][j]==1 && grap[j][i]==1) {
                cout<<mp2[i]<<" "<<mp2[j]<<endl;
                return 0;
            }
        }
        Floyd();
        if(mincircle==INF || mincircle<0)
            puts("SHIP IT");
        else {
            for(int i=k1-1; i>=0; --i)
                cout<<mp2[path[i]]<<" ";
            puts("");
        }
    
        return 0;
    }
    View Code

    K

    题意:有三种人,要过河,三种人分别有 b, n, e 个,每种人的系数是 sb,sn,se,有 (b+n+e)/2 条船,第 i 条船有速度 ci。每条船上坐两个人x, y ,这条船的速度就是 ci*(sx+sy)。要你分配人和船,使得速度最小的船的速度要尽可能大。

    tags:二分答案,然后O(n) 贪心 check。

    // k
    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a; i<=b; ++i)
    #define per(i,b,a) for (int i=b; i>=a; --i)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    #define MP make_pair
    #define PB push_back
    #define fi  first
    #define se  second
    typedef long long ll;
    const int N = 200005;
    
    int n, m, a[5], b[5], a2[5], c[N];
    int cnt;
    pair<int , pair<int, int >  > p[10];
    bool check(int x)
    {
        rep(i,1,3) a2[i]=a[i];
        rep(i,1,m)
        {
            bool flag=false;
            rep(j,1,cnt)
            {
                int t1=p[j].se.fi, t2=p[j].se.se;
                if(t1!=t2 && (a2[t1]<1 || a2[t2]<1)) continue;
                if(t1==t2 && a2[t1]<2) continue;
                if(c[i]*p[j].fi >= x)
                {
                    --a2[t1], --a2[t2];
                    flag=true;  break;
                }
            }
            if(!flag) return false;
        }
        return true;
    }
    int main()
    {
        rep(i,1,3) scanf("%d", &a[i]), m+=a[i];
        m /= 2;
        rep(i,1,3) scanf("%d", &b[i]);
        rep(i,1,m) scanf("%d", &c[i]);
        sort(c+1, c+1+m);
        rep(i,1,3) rep(j,i,3)
            p[++cnt]=MP(b[i]+b[j], MP(i,j));
        sort(p+1, p+1+cnt);
        int l=c[1]*(b[1]+b[1]), r=c[m]*(b[3]+b[3]), mid, ans;
        while(l<=r)
        {
            mid = l+r>>1;
            if(check(mid)) ans=mid, l=mid+1;
            else r=mid-1;
        }
        printf("%d
    ", ans);
    
        return 0;
    }
    View Code
  • 相关阅读:
    菜单范式
    PIC18F26K20
    单片机中串口通信模型
    STM8S103之GPIO
    STM8S103之ADC
    二叉树最近公共祖先
    全排列
    整数翻转
    完全二叉树节点个数
    二叉树的深度
  • 原文地址:https://www.cnblogs.com/sbfhy/p/8367121.html
Copyright © 2011-2022 走看看