2018 ACM-ICPC Syrian Collegiate Programming Contest(部分題解,待補)

傳送門

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]表示已經排好了狀態爲ii的測試點。枚舉接下來要選中的測試點jj,則有:d[i(1&lt;&lt;j)]=min(d[i]+cnt[i])d[i| (1&lt;&lt;j)]=min(d[i]+cnt[i])其中cnt[i]cnt[i]爲至少通過狀態ii的所有測試點的人數。
cnt[i]=jcnt[j](ij)cnt[i]=\sum_j cnt[j] (i\in j)其中最難的部分是求cnt[i]cnt[i],我所知道的枚舉子集的方法是O(t3)O(t^3),用SOS DP可以優化到O(t2t)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;0b[i]&gt;0的點,肯定是構成了一個環。將所有b[i]=0b[i]=0的點,按照a[i]a[i]從小到大排序,然後就是iijj連邊i&lt;j(i&lt;j);對於在環裏的a[i]b[i]&gt;0a[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;
}

Problem K: Tourists’ Tour
思路:先建好圖,用單調棧即可。然後就是如何塗色的問題了,可以想到所用的顏色種類不會很多(其實最多爲3種),然後我們把圖改爲有向圖,即高點向低點連邊,然後按照拓撲排序對各個點進行塗色。

#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;
vector<int>e[MAX];
int h[MAX],in[MAX];
int v[MAX];
int c[MAX][4];
void init(int n)
{
    stack<int>p;
    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);
        queue<int>p;
        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<e[x].size();j++)
                {
                    int nex=e[x][j];
                    c[nex][i]=1;
                    in[nex]--;
                    if(in[nex]==0)p.push(nex);
                }
                break;
            }
        }
        printf("%d\n",ans);
        for(int i=1;i<=n;i++)printf("%d%c",v[i],i==n?'\n':' ');
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章