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。
表示已经排好了状态为
的测试点。枚举接下来要选中的测试点
,则有:
其中
为至少通过状态
的所有测试点的人数。
即
其中最难的部分是求
,我所知道的枚举子集的方法是
,用SOS DP可以优化到
。
#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?
思路:所有
的点,肯定是构成了一个环。将所有
的点,按照
从小到大排序,然后就是
向
连边
;对于在环里的
的点,由环外的点向环内的点连边。
#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
思路:因为要求所有的点均在桌内且保证有解,先假设起点为
,那么我们可以求出一个半径最小的圆,恰好把机器人所有经过的点(包括起点)包含进去,然后我们就可以求出圆心,那么我们只要移动这个圆,使其圆心和原点重合即可。
那么起点也要移动相同的向量。
#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;
}