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\n", 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]='\0',  --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\n", ans);

    return 0;
}
View Code

 

posted @ 2018-02-04 19:36  v9fly  阅读(411)  评论(0编辑  收藏  举报