传送门
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]=j∑cnt[j](i∈j)其中最难的部分是求 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(t∗2t)。
#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