2018 ACM-ICPC Syrian Collegiate Programming Contest(部分题解,待补)

传送门

2018 ACM-ICPC 叙利亚大学生程序设计竞赛

Problem A: Hello SCPC 2018!
签到题
Problem B: Binary Hamming
简单题

#include
using namespace std;
const int MAX=1e6+10;
const int INF=1e9+7;
typedef long long ll;
char a[200],b[200];
int main()
{
    freopen("hamming.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s%s",a,b);
        sort(a,a+n);
        sort(b,b+n);
        reverse(a,a+n);

        int ans=0;
        for(int i=0;i

Problem C: Portals

思路:主要是找出起点和终点左右两边的第一个可达的传送门,然后分类讨论,细节比较多。(也可以找出传送门后,枚举把哪些’.‘变成’#’,然后BFS)

#include
using namespace std;
const int MAX=2e5+10;
const int INF=1e9+7;
typedef long long ll;
char s[MAX];
int check(int x,int y,char c)//找出x->y碰到的第一个c字符的位置
{
    if(x<=y)
    {
        for(int i=x;i<=y;i++)if(s[i]==c)return i;
    }
    else
    {
        for(int i=x;i>=y;i--)if(s[i]==c)return i;
    }
    return 0;
}
int ask(int x,int y)
{
    if(x<=y)
    {
        for(int i=x;i<=y;i++)
        {
            if(s[i]=='#')return 0;
            if(s[i]=='o')return i;
        }
    }
    else
    {
        for(int i=x;i>=y;i--)
        {
            if(s[i]=='#')return 0;
            if(s[i]=='o')return i;
        }
    }
    return 0;
}
int main()
{
    freopen("portals.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        int n,st,en;
        scanf("%d%s",&n,s+1);
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='s')st=i;
            if(s[i]=='e')en=i;
        }
        if(st>en)swap(st,en);
        if(check(st+1,en-1,'#')==0&&check(st+1,en-1,'.')==0){puts("-1");continue;}

        int sL=ask(st-1,1);    //st左边第一个可达的传送门
        int sR=ask(st+1,en-1); //st右边第一个可达的传送门
        int eL=ask(en-1,st+1); //en左边第一个可达的传送门
        int eR=ask(en+1,n);    //en右边第一个可达的传送门

        if(sR==0&&eL==0)
        {
            if(check(st+1,en-1,'#'))
            {
                if(sL==0||eR==0)puts("0");
                else if(sL==st-1&&eR==en+1)puts("-1");
                else puts("1");
            }
            else
            {
                if(sL==0||eR==0)puts("1");
                else if(sL==st-1&&eR==en+1)puts("-1");
                else puts("2");
            }
        }
        if(sR==0&&eL)
        {
            if(sL==0&&eR==0)puts("0");
            if(sL==0&&eR)puts("0");
            if(sL&&eR==0)
            {
                if(sL==st-1&&eL==en-1)puts("-1");
                else puts("1");
            }
            if(sL&&eR)
            {
                if(sLen+1)puts("1");
                else if(eR==en+1&&(sL==st-1||sR==st+1))puts("-1");
                else puts("2");
            }
        }
        if(sR&&eL)
        {
            if(sL==0&&eR==0)
            {
                if(eLst+1)puts("1");
                else puts("-1");
            }
            if(sL&&eR==0)
            {
                if((sL==st-1||sR==st+1)&&eL==en-1)puts("-1");
                else if(eLst+1)puts("1");
                else puts("2");
            }
            if(sL&&eR)
            {
                if((sL==st-1||sR==st+1)&&(eL==en-1||eR==en+1))puts("-1");
                else puts("2");
            }
        }
    }
    return 0;
}

Problem D: Carnival Slots
思路:贪心。按分数从大到小将列排序,然后从当前列开始向两边延伸。PS:因为一个沙雕错误错了几天,还一直没发现。顺带附上个人的一组数据:
3 6
1 1 1 1 1
\.\…
.\.\…
…\.\.
0 1 2 4 3 5

#include
using namespace std;
const int MAX=510;
typedef long long ll;
int a[MAX],v[MAX];
ll b[MAX],c[MAX];
char s[MAX][MAX];
int cmp(const int&x,const int&y){return c[x]>c[y];}
int main()
{
    freopen("balls.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)scanf("%lld",&b[i]);
        for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
        for(int i=1;i<=m;i++)scanf("%lld",&c[i]);
        for(int i=1;i<=m;i++)a[i]=i;
        sort(a+1,a+m+1,cmp);
        memset(v,0,sizeof v);
        ll ans=0;
        for(int i=1;i<=m;i++)
        {
            for(int j=n-1;j>=1;j--)s[j][a[i]]='.';
            if(v[a[i]]==0)ans+=b[a[i]]*c[a[i]];
            v[a[i]]=1;
            for(int x=a[i]-1,y=n;x>=1&&y>=1;x--,y--)
            {
                while(y>0&&s[y][x]=='.')y--;
                if(y==0)break;
                for(int j=y;j>=1;j--)s[j][x]='.';
                if(v[x]==0)ans+=b[x]*c[a[i]];
                v[x]=1;
            }
            for(int x=a[i]+1,y=n;x<=m&&y>=1;x++,y--)
            {
                while(y>0&&s[y][x]=='.')y--;
                if(y==0)break;
                for(int j=y;j>=1;j--)s[j][x]='.';
                if(v[x]==0)ans+=b[x]*c[a[i]];
                v[x]=1;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

Problem F: Pretests
思路:状压dp。 d [ i ] d[i] d[i]表示已经排好了状态为 i i i的测试点。枚举接下来要选中的测试点 j j j,则有: d [ i ∣ ( 1 < < j ) ] = m i n ( d [ i ] + c n t [ i ] ) d[i| (1<<j)]=min(d[i]+cnt[i]) d[i(1<<j)]=min(d[i]+cnt[i])其中 c n t [ i ] cnt[i] cnt[i]为至少通过状态 i i i的所有测试点的人数。
c n t [ i ] = ∑ j c n t [ j ] ( i ∈ j ) cnt[i]=\sum_j cnt[j] (i\in j) cnt[i]=jcnt[j](ij)其中最难的部分是求 c n t [ i ] cnt[i] cnt[i],我所知道的枚举子集的方法是 O ( t 3 ) O(t^3) O(t3),用SOS DP可以优化到 O ( t ∗ 2 t ) O(t*2^t) O(t2t)

#include
using namespace std;
const long long INF=1e9+7;
typedef long long ll;
char s[30];
ll cnt[1<<20];
ll d[1<<20];
string pre[1<<20];
int main()
{
    freopen("tests.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        int m,n;
        scanf("%d%d",&m,&n);
        for(int i=0;i<(1<=0;i--)
        for(int j=(1<=0;j--)
        {
            if((1<

Problem G: Is Topo Logical?
思路:所有 b [ i ] > 0 b[i]>0 b[i]>0的点,肯定是构成了一个环。将所有 b [ i ] = 0 b[i]=0 b[i]=0的点,按照 a [ i ] a[i] a[i]从小到大排序,然后就是 i i i j j j连边 ( i < j ) (i<j) i<j;对于在环里的 a [ i ] − b [ i ] > 0 a[i]-b[i]>0 a[i]b[i]>0的点,由环外的点向环内的点连边。

#include
using namespace std;
const int MOD=1e9+7;
const int MAX=2e5+10;
const double PI=acos(-1.0);
typedef long long ll;
vectorp,q;
int a[MAX],b[MAX];
int cmp(const int&x,const int&y){return a[x]0&&(int)q.size()-1<*max_element(b+1,b+n+1))return 0;
    for(int i=0;i(int)p.size())return 0;
    return 1;
}
int main()
{
    freopen("topo.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        int n,ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)scanf("%d",&b[i]);
        if(check(n)==0){puts("-1");continue;}
        for(int i=1;i<=n;i++)ans+=a[i];
        printf("%d\n",ans);
        for(int i=q.size()-1;i>=0;i--)printf("%d %d\n",q[(i-1+(int)q.size())%(int)q.size()],q[i]);
        for(int i=0;i

Problem H: Bugged System
思路:因为要保证每个人最后都没有付钱,那么每个人的起点是另一个人的终点,最后形成一个环。如果形成了环,那么最短路就是所有人的路径和;否则输出-1。

#include
using namespace std;
const int MAX=1e6+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
ll x[MAX];
int a[MAX];
int main()
{
    freopen("bugged.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%lld",&x[i]);
        for(int i=1;i<=n;i++)a[i]=0;
        ll ans=0;
        for(int i=1;i<=m;i++)
        {
            int L,R;
            scanf("%d%d",&L,&R);
            a[L]++;
            a[R]--;
            ans+=abs(x[R]-x[L]);
        }
        for(int i=1;i<=n;i++)if(a[i]!=0)ans=-1;
        printf("%lld\n",ans);
    }
    return 0;
}

Problem I: Rise of the Robots
思路:因为要求所有的点均在桌内且保证有解,先假设起点为 ( 0 , 0 ) (0,0) (0,0),那么我们可以求出一个半径最小的圆,恰好把机器人所有经过的点(包括起点)包含进去,然后我们就可以求出圆心,那么我们只要移动这个圆,使其圆心和原点重合即可。
那么起点也要移动相同的向量。

#include
using namespace std;
const int MAX=300;
struct Point{double x,y;}p[MAX],ans;
Point operator+(Point A,Point B){return (Point){A.x+B.x,A.y+B.y};}
double operator*(Point A,Point B){return A.x*B.x+A.y*B.y;}
double dis(Point A,Point B){return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));}
int n;
double cal(double x,double y)
{
    double sum=0;
    for(int i=0;i<=n;i++)sum=max(sum,dis((Point){x,y},p[i]));
    return sum;
}
double cal(double x)
{
    double l=-100000,r=100000;
    for(int i=1;i<=100;i++)
    {
        double mid=(l+r)/2;
        double m=(mid+r)/2;
        if(cal(x,mid)>cal(x,m))l=mid;
        else r=m;
    }
    return cal(x,(l+r)/2);
}
int main()
{
    freopen("robots.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        int R,R1;
        scanf("%d%d%d",&n,&R,&R1);
        p[0]={0,0};
        for(int i=1;i<=n;i++)scanf("%lf%lf",&p[i].x,&p[i].y);
        for(int i=1;i<=n;i++)p[i]=p[i-1]+p[i];
        double l=-R,r=R;
        for(int i=1;i<=100;i++)//三分求出包含所有点的最小圆的圆心
        {
            double mid=(l+r)/2;
            double m=(mid+r)/2;
            if(cal(mid)>cal(m))l=mid;
            else r=m;
        }
        ans.x=(l+r)/2;
        l=-R,r=R;
        for(int i=1;i<=100;i++)
        {
            double mid=(l+r)/2;
            double m=(mid+r)/2;
            if(cal(ans.x,mid)>cal(ans.x,m))l=mid;
            else r=m;
        }
        ans.y=(l+r)/2;
        printf("%.9f %.9f\n",-ans.x,-ans.y);
    }
    return 0;
}

Problem K: Tourists’ Tour
思路:先建好图,用单调栈即可。然后就是如何涂色的问题了,可以想到所用的颜色种类不会很多(其实最多为3种),然后我们把图改为有向图,即高点向低点连边,然后按照拓扑排序对各个点进行涂色。

#include
using namespace std;
const int MAX=1e6+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
vectore[MAX];
int h[MAX],in[MAX];
int v[MAX];
int c[MAX][4];
void init(int n)
{
    stackp;
    for(int i=1;i<=n;i++)e[i].clear();
    for(int i=1;i<=n;i++)in[i]=0;
    while(!p.empty())p.pop();
    for(int i=1;i<=n;i++)
    {
        while(!p.empty()&&h[p.top()]<=h[i])p.pop();
        if(!p.empty())e[p.top()].push_back(i),in[i]++;
        p.push(i);
    }
    while(!p.empty())p.pop();
    for(int i=n;i>=1;i--)
    {
        while(!p.empty()&&h[p.top()]<=h[i])p.pop();
        if(!p.empty())e[p.top()].push_back(i),in[i]++;
        p.push(i);
    }
    for(int i=1;i<=n;i++)
    for(int j=0;j<=3;j++)c[i][j]=0;
}
int main()
{
    freopen("tour.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&h[i]);
        init(n);
        queuep;
        for(int i=1;i<=n;i++)if(in[i]==0)p.push(i);
        int ans=0;
        while(!p.empty())
        {
            int x=p.front();p.pop();
            for(int i=1;i<=3;i++)
            {
                if(c[x][i])continue;
                ans=max(ans,i);
                v[x]=i;
                for(int j=0;j

你可能感兴趣的:(思维,计算几何,状压DP,ACM-ICPC)