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

版权声明:http://blog.csdn.net/Mitsuha_。 https://blog.csdn.net/Mitsuha_/article/details/84455292

传送门

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

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

#include<bits/stdc++.h>
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<n;i++)ans+=a[i]!=b[i];
        printf("%d\n",ans);
    }
    return 0;
}

Problem C: Portals

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

#include<bits/stdc++.h>
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(sL<st-1)puts("1");
                else if(sL==st-1&&(eL==en-1||eR==en+1))puts("-1");
                else puts("2");
            }
        }
        if(sR&&eL==0)
        {
            if(sL==0&&eR==0)puts("0");
            if(sL==0&&eR)
            {
                if(sR==st+1&&eR==en+1)puts("-1");
                else puts("1");
            }
            if(sL&&eR==0)puts("0");
            if(sL&&eR)
            {
                if(eR>en+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(eL<en-1||sR>st+1)puts("1");
                else puts("-1");
            }
            if(sL&&eR==0)
            {
                if((sL==st-1||sR==st+1)&&eL==en-1)puts("-1");
                else if(eL<en-1)puts("1");
                else puts("2");
            }
            if(sL==0&&eR)
            {
                if((eL==en-1||eR==en+1)&&sR==st+1)puts("-1");
                else if(sR>st+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<bits/stdc++.h>
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] 表示已经排好了状态为 i i 的测试点。枚举接下来要选中的测试点 j j ,则有: d [ i ( 1 &lt; &lt; j ) ] = m i n ( d [ i ] + c n t [ i ] ) d[i| (1&lt;&lt;j)]=min(d[i]+cnt[i]) 其中 c n t [ i ] cnt[i] 为至少通过状态 i i 的所有测试点的人数。
c n t [ i ] = j c n t [ j ] ( i j ) cnt[i]=\sum_j cnt[j] (i\in j) 其中最难的部分是求 c n t [ i ] cnt[i] ,我所知道的枚举子集的方法是 O ( t 3 ) O(t^3) ,用SOS DP可以优化到 O ( t 2 t ) O(t*2^t)

#include<bits/stdc++.h>
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<<m);i++)cnt[i]=0;
        for(int i=0;i<(1<<m);i++)d[i]=INF;
        for(int i=0;i<(1<<m);i++)pre[i]="";
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            int sum=0;
            for(int j=0;j<m;j++)sum+=(1<<j)*(s[j]-'0');
            cnt[sum]++;
        }
        //SOS dp预处理出cnt[i]
        for(int i=m-1;i>=0;i--)
        for(int j=(1<<m)-1;j>=0;j--)
        {
            if((1<<i)&j)cnt[j^(1<<i)]+=cnt[j];
        }
        d[0]=0;
        for(int i=0;i<(1<<m);i++)
        {
            for(int j=0;j<m;j++)
            {
                if(i&(1<<j))continue;
                if(d[i]+cnt[i]<d[i^(1<<j)])
                {
                    d[i^(1<<j)]=d[i]+cnt[i];
                    pre[i^(1<<j)]=pre[i];
                    pre[i^(1<<j)]+=j+1+'0';
                }
                else if(d[i]+cnt[i]==d[i^(1<<j)])
                {
                    string a=pre[i];
                    a+=j+1+'0';
                    if(a<pre[i^(1<<j)])pre[i^(1<<j)]=a;
                }
            }
        }
        printf("%lld\n",d[(1<<m)-1]);
        for(int i=0;i<m;i++)printf("%d%c",pre[(1<<m)-1][i]-'0',i==m-1?'\n':' ');
    }
    return 0;
}

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

#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAX=2e5+10;
const double PI=acos(-1.0);
typedef long long ll;
vector<int>p,q;
int a[MAX],b[MAX];
int cmp(const int&x,const int&y){return a[x]<a[y];}
int check(int n)
{
    p.clear();
    q.clear();
    for(int i=1;i<=n;i++)
    {
        if(b[i]!=0)q.push_back(i);
        else p.push_back(i);
    }
    sort(p.begin(),p.end(),cmp);
    if(q.size()>0&&(int)q.size()-1<*max_element(b+1,b+n+1))return 0;
    for(int i=0;i<p.size();i++)if(i<a[p[i]])return 0;
    for(int i=0;i<q.size();i++)if(a[q[i]]-b[q[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<q.size();i++)
        for(int j=i+1;j<i+b[q[i]];j++)printf("%d %d\n",q[j%(int)q.size()],q[i]);
        for(int i=0;i<q.size();i++)
        for(int j=0;j<a[q[i]]-b[q[i]];j++)printf("%d %d\n",p[j],q[i]);
        for(int i=0;i<p.size();i++)
        for(int j=0;j<a[p[i]];j++)printf("%d %d\n",p[j],p[i]);
    }
    return 0;
}

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

#include<bits/stdc++.h>
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) ,那么我们可以求出一个半径最小的圆,恰好把机器人所有经过的点(包括起点)包含进去,然后我们就可以求出圆心,那么我们只要移动这个圆,使其圆心和原点重合即可。
那么起点也要移动相同的向量。

#include<bits/stdc++.h>
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;
}

猜你喜欢

转载自blog.csdn.net/Mitsuha_/article/details/84455292