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

非 Y 不嫁゛ 提交于 2020-02-12 12:24:55

Time:2018.4.29    8:30-13:30

Link


A

题意

给出总的路程L,普通速度a,喝咖啡速度后b,等咖啡时间t,咖啡有作用时间r,和n个咖啡厅的位置。买咖啡需要等待,喝咖啡时速度提升

问在哪几个咖啡厅买咖啡,走完总路程所花的时间最短。可以扔掉咖啡,可以用手机预定咖啡。(L<=1e11,a,b<=200,t<=300,r<=1200,n<=5e5)

假如不可以预定怎么做?

分析

Difficult

ym:单调队列优化后dp???,留坑


B    solved by ym

题意

四人接力跑,现分别给出n个人的第一棒和其他棒的时间,问选择四个人所用时间最小的时间,和对应的人(即方案) (n<=500)

分析

ym:首先直观的n^4暴力枚举,肯定不行,枚举第一棒贪心选取其余的即可

时间复杂度O(nlogn)


C

题意

分析

大大大模拟,留坑 


D     solved by ym&czh

题意

给出n个长度为m的0/1串,0和0或者1和1都是相同,问找出一个字符串使得与其他所有串相同的的最多的最少(n<=1e5,m<=20)

分析

ym:相同的数量尽可能少==不同的数量尽可能多,故可以将每个串看做一个点出发从其BFS找所有没有出现过的点,找一个different最大

         为什么different最大的是答案?我们BFS找到的每一个点都是原来的点最小different

时间复杂度:O(k*2^k)

czh:将每种字符串看作是一个点,总共2^20=1e6个点,以输入的n个字符串为起点,bfs找到最远的点

这题用了个sting加map,运算起来特别慢,以后要首先分析时间复杂度

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
#include <cmath>
#include <queue>
#include <ctime>
using namespace std;
#define lson l,(l+r)/2,rt<<1
#define rson (l+r)/2+1,r,rt<<1|1
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define pb push_back
#define fi first
#define se second
#define ll long long
#define sz(x) (int)(x).size()
#define pll pair<long long,long long>
#define pii pair<int,int>
#define pq priority_queue

int d[1<<20];
queue<int>q;
int n,k;

void bfs()
{
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<k;i++)
        {
            int v=u^(1<<i);
            if(d[v]>d[u]+1)
            {
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
}

int main()
{
    string s;
    cin>>n>>k;
    for(int i=0;i<(1<<20);i++)
        d[i]=1<<20+1;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        int sum=0;
        for(int ii=0;ii<k;ii++)
        {
            if(s[ii]=='1')
            sum+=(1<<(k-ii-1));
        }
        d[sum]=0;
        q.push(sum);
    }
    bfs();
    int id=0;
    for(int i=0;i<(1<<k);i++)
    {
        if(d[i]>d[id])
        {
            id=i;
        }
    }
    for(int i=0;i<k;i++)
    {
        if((id>>(k-i-1))&1)
            cout<<1;
        else
            cout<<0;
    }
    cout<<endl;
    return 0;
}

E    solved by ym&czh

题意

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

分析

ym:从排水口出发BFS,排水口是终点,每一个点每次只要可以进行类似松弛操作就update,但不幸,TEL,因为一个点可能被update很多次并且每次update后都要从其出发继续update

        优化:一个较为显然的是,每次从较低的从出发update最优,思想类似最短路,每次从边权最小的出发

        裸的BFS找最短路适用于边权都一样的,才能保证复杂度为O(v+e),因为每个点只会访问一次(第一次访问的一定是最小的),回到此题,高度和边权类似,但“边权”不一样

        所以时间复杂度会爆炸

时间复杂度(nlogn)

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int mod=1e9+7;

const int dx[]={1,-1,0,0,1,1,-1,-1};
const int dy[]={0,0,-1,1,-1,1,1,-1};

int mp[505][505];
int h,w,ans[505][505];
int sx,sy;

struct node
{
    int x,y,h;
    friend bool operator<(node a,node b)
    {
        return a.h>b.h;
    }
};

bool ok(int x,int y)
{
    if(x>=1&&x<=h&&y>=1&&y<=w)
        return true;
    return false;
}
void bfs(int x,int y,int h)
{
    priority_queue<node>q;
    q.push({x,y,h});
    while(!q.empty())
    {
        node k=q.top();
        q.pop();
        for(int i=0;i<8;i++)
        {
            int xx=k.x+dx[i];
            int yy=k.y+dy[i];
            if(ok(xx,yy) && ans[xx][yy]>max(k.h,mp[xx][yy]))
            {
                ans[xx][yy]=max(k.h,mp[xx][yy]);
                q.push({xx,yy,ans[xx][yy]});
            }
        }
    }

}

int main()
{
    scanf("%d%d",&h,&w);
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=w;j++)
        {
            scanf("%d",&mp[i][j]);
            if(mp[i][j]>0)
                mp[i][j]=0;
        }
    }
    scanf("%d%d",&sx,&sy);
    if(mp[sx][sy]==0)
    {
        cout<<0<<endl;
        return 0;
    }
    ans[sx][sy]=mp[sx][sy];
    bfs(sx,sy,mp[sx][sy]);
    ll answer=0;
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=w;j++)
        {
            answer+=ans[i][j];
        }
    }
    cout<<-answer<<endl;
    return 0;
}

F

Different


G     solved by czh&ym

题意

有n个队伍,m条事件。每条事件代表a队伍通过一个题目,罚时为b。对于每条事件,输出,1号队伍的排名。 

分析

czh:用一个set数组储存每个过题数的队伍编号。对于每次询问,如果不是一号队伍,只需要挪动set中的元素,如果是一号队伍,统计这次过题超过的队伍数,并挪动

ym:写了暴力复杂度爆炸成功TLE,还好帮忙czh找出错误样例

花絮:tm不能当变量名啊!!!

时间复杂度:由于和1同题数最多的队伍很多,并且修改的是1后就不想同的,所以很快(我算不出qaq)


H

题意

分析

ym:听说是极角排序后最大流,滚去学


I     solved by ym

题意

问题可以简化为:给出n个点的有向图,问最小环的路径(即方案)(n<=500)

分析

ym:dfs标记找环??X掉)       从每个点出发BFS,求出最短路的过程中记录前驱即可,既然都是环,随便输出都行      /     当然Floyd也可以

dfs找环存在的问题:由于序号标记是按照dfs顺序,故无法得出环的边数,但可以求是否存在环

时间复杂度O(n*(n+m))

#include<bits/stdc++.h>
using namespace std;


vector<int>v[502],rv[502];
unordered_map<string,int>mp;
int d[502];
int pre[502];
char name[502][10];

void bfs(int st)
{
    queue<int>q;
    q.push(st);
    d[st]=0;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=0;i<int(v[now].size());i++)
        {
            int to=v[now][i];
            if(d[to]>d[now]+1)
            {
                d[to]=d[now]+1;
                pre[to]=now;
                q.push(to);
            }
        }
    }

}

int main()
{
    int n;
    string s;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",name[i]);
        mp[name[i]]=i;
    }
    int x=n,k;
    while(x--)
    {
        cin>>s;
        scanf("%d",&k);
        int u=mp[s];
        while(k--)
        {
          cin>>s;
            while(1)
            {
                cin>>s;
            int len=s.length();
            if(s[len-1]==',')
            {
                s.resize(len-1);
                int to=mp[s];
                v[to].push_back(u);
                rv[u].push_back(to);
            }
            else
            {
                int to=mp[s];
                v[to].push_back(u);
                rv[u].push_back(to);
                break;
            }
            }
        }
    }
    int ans=1e9,st,ed;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            d[j]=1e9;
        bfs(i);
        for(int j=0;j<int(rv[i].size());j++)
        {
            int u=rv[i][j];
            if(ans>d[u]+1)
            {
                ans=d[u]+1;
                st=i;
                ed=u;
            }
        }
    }
    if(ans==1e9)
    {
        puts("SHIP IT");
        return 0;
    }
    else
    {

        for(int j=1;j<=n;j++)
            d[j]=1e9;
        bfs(st);
        int now=ed;
        while(now!=st)
        {
            printf("%s ",name[now]);
            now=pre[now];
        }
        printf("%s\n",name[now]);
    }
    return 0;
}

J   solved by ym

题意

签到,按题意模拟即可


K  solved by ym

题意

给出三种人的数量b,n,e,每种人有一个Sb,Sn,Se,现两人一组,给出(b+n+e)/2个vi,问要所有组最小的最大方案,输出最小的最大

分析

ym:二分答案后贪心暴力check

时间复杂度O(nlogn)

#include<bits/stdc++.h>
using namespace std;

int B,N,E;
int a[1000005],w[5],m[5];
int sb,sn,se,n;

bool check(int mid)
{
    int mm[5];
    mm[1]=m[1], mm[2]=m[2], mm[3]=m[3];
    for(int i=1;i<=n;i++)
    {
        int jj=-1,kk=-1;
        int need=mid/a[i]+(mid%a[i]>0);
        for(int j=1;j<=3;j++)
        {
            for(int k=j;k<=3;k++)
            {
                int num=1;
                if(j==k) num=2;
                if(jj==-1&&kk==-1)
                {
                   if(mm[j]>=num && mm[k]>=num && (w[j]+w[k])>=need)
                   {
                       jj=j;
                       kk=k;
                   }
                }
                else
                {
                    if(mm[j]>=num && mm[k]>=num && (w[j]+w[k])>=need && (w[j]+w[k])<(w[jj]+w[kk]))
                    {
                        jj=j;
                        kk=k;
                    }
                }
            }
        }
        if(jj==-1)
            return false;
        mm[jj]-=1;
        mm[kk]-=1;
    }
    return true;
}

int main()
{
    scanf("%d%d%d",&m[1],&m[2],&m[3]);
    scanf("%d%d%d",&w[1],&w[2],&w[3]);
    n=(m[1]+m[2]+m[3])/2;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    int l=1,r=1e9;
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    printf("%d\n", l);
    return 0;
}

Summary

Ym

Czh:

Time:2018.4.29    8:30-13:30

Link


A

题意

给出总的路程L,普通速度a,喝咖啡速度后b,等咖啡时间t,咖啡有作用时间r,和n个咖啡厅的位置。买咖啡需要等待,喝咖啡时速度提升

问在哪几个咖啡厅买咖啡,走完总路程所花的时间最短。可以扔掉咖啡,可以用手机预定咖啡。(L<=1e11,a,b<=200,t<=300,r<=1200,n<=5e5)

假如不可以预定怎么做?

分析

Difficult

ym:单调队列优化后dp???,留坑


B    solved by ym

题意

四人接力跑,现分别给出n个人的第一棒和其他棒的时间,问选择四个人所用时间最小的时间,和对应的人(即方案) (n<=500)

分析

ym:首先直观的n^4暴力枚举,肯定不行,枚举第一棒贪心选取其余的即可

时间复杂度O(nlogn)


C

题意

分析

大大大模拟,留坑 


D     solved by ym&czh

题意

给出n个长度为m的0/1串,0和0或者1和1都是相同,问找出一个字符串使得与其他所有串相同的的最多的最少(n<=1e5,m<=20)

分析

ym:相同的数量尽可能少==不同的数量尽可能多,故可以将每个串看做一个点出发从其BFS找所有没有出现过的点,找一个different最大

         为什么different最大的是答案?我们BFS找到的每一个点都是原来的点最小different

时间复杂度:O(k*2^k)

czh:将每种字符串看作是一个点,总共2^20=1e6个点,以输入的n个字符串为起点,bfs找到最远的点

这题用了个sting加map,运算起来特别慢,以后要首先分析时间复杂度

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <iostream>
#include <cmath>
#include <queue>
#include <ctime>
using namespace std;
#define lson l,(l+r)/2,rt<<1
#define rson (l+r)/2+1,r,rt<<1|1
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define pb push_back
#define fi first
#define se second
#define ll long long
#define sz(x) (int)(x).size()
#define pll pair<long long,long long>
#define pii pair<int,int>
#define pq priority_queue

int d[1<<20];
queue<int>q;
int n,k;

void bfs()
{
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<k;i++)
        {
            int v=u^(1<<i);
            if(d[v]>d[u]+1)
            {
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
}

int main()
{
    string s;
    cin>>n>>k;
    for(int i=0;i<(1<<20);i++)
        d[i]=1<<20+1;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        int sum=0;
        for(int ii=0;ii<k;ii++)
        {
            if(s[ii]=='1')
            sum+=(1<<(k-ii-1));
        }
        d[sum]=0;
        q.push(sum);
    }
    bfs();
    int id=0;
    for(int i=0;i<(1<<k);i++)
    {
        if(d[i]>d[id])
        {
            id=i;
        }
    }
    for(int i=0;i<k;i++)
    {
        if((id>>(k-i-1))&1)
            cout<<1;
        else
            cout<<0;
    }
    cout<<endl;
    return 0;
}

E    solved by ym&czh

题意

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

分析

ym:从排水口出发BFS,排水口是终点,每一个点每次只要可以进行类似松弛操作就update,但不幸,TEL,因为一个点可能被update很多次并且每次update后都要从其出发继续update

        优化:一个较为显然的是,每次从较低的从出发update最优,思想类似最短路,每次从边权最小的出发

        裸的BFS找最短路适用于边权都一样的,才能保证复杂度为O(v+e),因为每个点只会访问一次(第一次访问的一定是最小的),回到此题,高度和边权类似,但“边权”不一样

        所以时间复杂度会爆炸

时间复杂度(nlogn)

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int mod=1e9+7;

const int dx[]={1,-1,0,0,1,1,-1,-1};
const int dy[]={0,0,-1,1,-1,1,1,-1};

int mp[505][505];
int h,w,ans[505][505];
int sx,sy;

struct node
{
    int x,y,h;
    friend bool operator<(node a,node b)
    {
        return a.h>b.h;
    }
};

bool ok(int x,int y)
{
    if(x>=1&&x<=h&&y>=1&&y<=w)
        return true;
    return false;
}
void bfs(int x,int y,int h)
{
    priority_queue<node>q;
    q.push({x,y,h});
    while(!q.empty())
    {
        node k=q.top();
        q.pop();
        for(int i=0;i<8;i++)
        {
            int xx=k.x+dx[i];
            int yy=k.y+dy[i];
            if(ok(xx,yy) && ans[xx][yy]>max(k.h,mp[xx][yy]))
            {
                ans[xx][yy]=max(k.h,mp[xx][yy]);
                q.push({xx,yy,ans[xx][yy]});
            }
        }
    }

}

int main()
{
    scanf("%d%d",&h,&w);
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=w;j++)
        {
            scanf("%d",&mp[i][j]);
            if(mp[i][j]>0)
                mp[i][j]=0;
        }
    }
    scanf("%d%d",&sx,&sy);
    if(mp[sx][sy]==0)
    {
        cout<<0<<endl;
        return 0;
    }
    ans[sx][sy]=mp[sx][sy];
    bfs(sx,sy,mp[sx][sy]);
    ll answer=0;
    for(int i=1;i<=h;i++)
    {
        for(int j=1;j<=w;j++)
        {
            answer+=ans[i][j];
        }
    }
    cout<<-answer<<endl;
    return 0;
}

F

Different


G     solved by czh&ym

题意

有n个队伍,m条事件。每条事件代表a队伍通过一个题目,罚时为b。对于每条事件,输出,1号队伍的排名。 

分析

czh:用一个set数组储存每个过题数的队伍编号。对于每次询问,如果不是一号队伍,只需要挪动set中的元素,如果是一号队伍,统计这次过题超过的队伍数,并挪动

ym:写了暴力复杂度爆炸成功TLE,还好帮忙czh找出错误样例

花絮:tm不能当变量名啊!!!

时间复杂度:由于和1同题数最多的队伍很多,并且修改的是1后就不想同的,所以很快(我算不出qaq)


H

题意

分析

ym:听说是极角排序后最大流,滚去学


I     solved by ym

题意

问题可以简化为:给出n个点的有向图,问最小环的路径(即方案)(n<=500)

分析

ym:dfs标记找环??X掉)       从每个点出发BFS,求出最短路的过程中记录前驱即可,既然都是环,随便输出都行      /     当然Floyd也可以

dfs找环存在的问题:由于序号标记是按照dfs顺序,故无法得出环的边数,但可以求是否存在环

时间复杂度O(n*(n+m))

#include<bits/stdc++.h>
using namespace std;


vector<int>v[502],rv[502];
unordered_map<string,int>mp;
int d[502];
int pre[502];
char name[502][10];

void bfs(int st)
{
    queue<int>q;
    q.push(st);
    d[st]=0;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=0;i<int(v[now].size());i++)
        {
            int to=v[now][i];
            if(d[to]>d[now]+1)
            {
                d[to]=d[now]+1;
                pre[to]=now;
                q.push(to);
            }
        }
    }

}

int main()
{
    int n;
    string s;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",name[i]);
        mp[name[i]]=i;
    }
    int x=n,k;
    while(x--)
    {
        cin>>s;
        scanf("%d",&k);
        int u=mp[s];
        while(k--)
        {
          cin>>s;
            while(1)
            {
                cin>>s;
            int len=s.length();
            if(s[len-1]==',')
            {
                s.resize(len-1);
                int to=mp[s];
                v[to].push_back(u);
                rv[u].push_back(to);
            }
            else
            {
                int to=mp[s];
                v[to].push_back(u);
                rv[u].push_back(to);
                break;
            }
            }
        }
    }
    int ans=1e9,st,ed;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            d[j]=1e9;
        bfs(i);
        for(int j=0;j<int(rv[i].size());j++)
        {
            int u=rv[i][j];
            if(ans>d[u]+1)
            {
                ans=d[u]+1;
                st=i;
                ed=u;
            }
        }
    }
    if(ans==1e9)
    {
        puts("SHIP IT");
        return 0;
    }
    else
    {

        for(int j=1;j<=n;j++)
            d[j]=1e9;
        bfs(st);
        int now=ed;
        while(now!=st)
        {
            printf("%s ",name[now]);
            now=pre[now];
        }
        printf("%s\n",name[now]);
    }
    return 0;
}

J   solved by ym

题意

签到,按题意模拟即可


K  solved by ym

题意

给出三种人的数量b,n,e,每种人有一个Sb,Sn,Se,现两人一组,给出(b+n+e)/2个vi,问要所有组最小的最大方案,输出最小的最大

分析

ym:二分答案后贪心暴力check

时间复杂度O(nlogn)

#include<bits/stdc++.h>
using namespace std;

int B,N,E;
int a[1000005],w[5],m[5];
int sb,sn,se,n;

bool check(int mid)
{
    int mm[5];
    mm[1]=m[1], mm[2]=m[2], mm[3]=m[3];
    for(int i=1;i<=n;i++)
    {
        int jj=-1,kk=-1;
        int need=mid/a[i]+(mid%a[i]>0);
        for(int j=1;j<=3;j++)
        {
            for(int k=j;k<=3;k++)
            {
                int num=1;
                if(j==k) num=2;
                if(jj==-1&&kk==-1)
                {
                   if(mm[j]>=num && mm[k]>=num && (w[j]+w[k])>=need)
                   {
                       jj=j;
                       kk=k;
                   }
                }
                else
                {
                    if(mm[j]>=num && mm[k]>=num && (w[j]+w[k])>=need && (w[j]+w[k])<(w[jj]+w[kk]))
                    {
                        jj=j;
                        kk=k;
                    }
                }
            }
        }
        if(jj==-1)
            return false;
        mm[jj]-=1;
        mm[kk]-=1;
    }
    return true;
}

int main()
{
    scanf("%d%d%d",&m[1],&m[2],&m[3]);
    scanf("%d%d%d",&w[1],&w[2],&w[3]);
    n=(m[1]+m[2]+m[3])/2;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    int l=1,r=1e9;
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    printf("%d\n", l);
    return 0;
}

Summary

Ym

Czh:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!