La Salle-Pui Ching Programming Challenge 培正喇沙編程挑戰賽 2016 A~F

La Salle-Pui Ching Programming Challenge 培正喇沙編程挑戰賽 2016 A~F

文章目录

  • La Salle-Pui Ching Programming Challenge 培正喇沙編程挑戰賽 2016 A~F
    • A.Shuttle Bus
    • B.Salt Trading
    • C.Annoying Mathematics
    • D.Archery
    • E.Bacteria Experiment
    • F.Anniversaries

A.Shuttle Bus

给定一个2*n的方格,从左上角开始走有些各自不能走,问能不能一次遍历所有能走的方格。
第一个点可以通过的条件:x同,则y的间距为奇数,x不同,则y的间距为偶数。
不是第一个点,上条件取非
如果最后一行全为教堂也可走完。

#include
#define MAXN 5010
using namespace std;
struct pp 
{
	int x,y;
}c[MAXN];
int n,m;
bool cmp(pp a,pp b)
{
	return a.x<b.x;
} 
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(int i=0;i<m;i++)
			scanf("%d%d",&c[i].y,&c[i].x);
		sort(c,c+m,cmp);
		bool flag=true;
		if(c[0].x%2==0&&c[0].y==2) flag=false;
		if(c[0].x%2==1&&c[0].y==1) flag=false;
		if(flag)
		{
			for(int i=m-1;i>=1;)
			{ 
				if(c[i-1].x==c[i].x&&c[i].x==n)
					n--,i-=2,m-=2;
				else if(c[i-1].x==c[i].x&&c[i].x!=n)
				{
					flag=false;break;
				}
				else i--;
			}
		}
			
		if(flag)
		{
			for(int i=1;i<m;i++)
			{
				if(c[i-1].y==c[i].y&&(c[i].x-c[i-1].x)%2==0)
					{
						flag=false;break;
					}
				else if(c[i-1].y!=c[i].y&&(c[i].x-c[i-1].x)%2==1)
					{
						flag=false;break;
					}
			}
		}
			
		if(flag) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

B.Salt Trading

题意 就是进了100货 进口价1 要分配出口到三个地方 给出三个地方的售价 要求保证每一个地方的销售额都大于100
三个地方的售价是1到1000的实数 保证只给两位小数 给三个地方分配的货物可以以任意精度分配
算法 模拟
解题
由题意推出要确保1/A+1/B+1/C<1,可用1/A+1/B+1/C<1-ε来判别,ε取到2×10-11左右以下即可

#include
using namespace std;

int main()
{
	double a,b,c;
	long long d,e,f,g;
	cin>>a>>b>>c;
	if(1/a+1/b+1/c<1-1e-12) cout<<"Yes";
	else cout<<"No";
	return 0;
}

C.Annoying Mathematics

题意 求N个数于1…R中,他们的最小公倍数为K.
算法 搜索
解题 首先对K分解质因数.先求出1…R中最少的数使得其满足最小公倍数为K,将分解质因数得到的相同因子乘在一起.如K=60=22x3x5,先得到S={3,4,5}.而后再搜索可以把他减小的方案,其中若有两个数乘积小于等于R,可尝试将其乘起来,如R=12时,{3,4,5}可变为{5,12}.从而找到数的最小个数Min.若Min>N,则不能满足条件,若Min 程序(未通过)求大佬找错

#include
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
LL k,q,j,i,nv,nm,r,n,Min;
LL a[2100],b[2100],c[2100],d[2100],e[2100];
set<LL>s;
void dfs2(LL d[],LL nm)
{
 sort(d+1,d+1+nv);
 if ((d[1]*d[2]>r&&nm<Min)||nm==1)
 {
  Min=nm;
  for(LL i=1;i<=nm;i++)
  c[i]=d[i];
 }
 else 
 {
  for(LL i=2;i<=nm&&d[i]*d[1]<=r;i++)
  {
   for(LL j=1;j<=nm-1;j++)
   {
    if(j+1==i) e[j]=d[i]*d[1];
    else e[j]=d[j+1];
   }
   dfs2(e,nm-1);
  }
 }
 return;
}
void dfs(LL l,LL v,LL ss)
{
 LL temp=0;
 c[l]=v;
 if(s.size()>=n||ss>r) return;
 if (l>=nm) s.insert(ss);
 else 
 for(LL i=0;i<=a[l+1];i++)
 {
  if(temp) temp=temp*b[l+1];
  else temp=1;
  dfs(l+1,i,temp*ss);
 }
 return;
}

int main()
{
 n=read();r=read();k=read();
 q=0; j=0; i=2;
 while(k>1)
 {
  if(k%i) i++;
  else {
   k=k/i;
      if(i>q)
	  {
    j++;
    b[j]=i;
    q=i;
    c[j]=1;
   }
   c[j]=i*c[j];
   a[j]++;
  }
 }
 
 sort(c+1,c+1+j);
 if(c[j]>r) {
  cout<<"-1";
  return 0;
 }
 nm=j; Min=j;
 dfs2(c,j);
 
 if(Min>n) {
  cout<<"-1";
  return 0;
 }
 
 for(LL i=1;i<=Min;i++)
 s.insert(c[i]);
 
 LL temp=0;
 nm=j;
 for(LL i=0;i<=a[1];i++)
 {
  if(temp) temp=temp*b[1];
  else temp=1;
  dfs(1,i,temp);
 }
 
 if(s.size()<n) 
 {
  cout<<"-1";
  return 0;
 }
 
 for()
 return 0;
} 

D.Archery

两个人比赛射箭,M为0分,x为10分,110为110分
先看分数,分多的赢,如果一样,看10与x,多的赢,如果依然一样,看x多的赢,再一样shoot-off

#include
#define NMAX 0x7fffffff
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
int main()
{
	int n=read();LL sc=0,shi=0,pl=0;
	for(int i=1;i<=n;++i)
	{
		char cc=getchar();
		while(cc=='\n')cc=getchar();
		if(cc=='1'&&getchar()=='0'){shi++;sc+=10;}
		else if(cc=='X'){shi++;pl++;sc+=10;}
		else if(cc=='M');
		else sc+=cc-'0';
	}
	for(int i=1;i<=n;++i)
	{
		char cc=getchar();
		while(cc=='\n')cc=getchar();
		if(cc=='1'&&getchar()=='0'){shi--;sc-=10;}
		else if(cc=='X'){shi--;pl--;sc-=10;}
		else if(cc=='M');
		else sc-=cc-'0';
	}
	if(sc>0)printf("Yuju");
	else if(sc<0)printf("Yerin");
	else 
	{
		if(shi>0)printf("Yuju");
    	else if(shi<0)printf("Yerin");
    	else 
    	{
    		if(pl>0)printf("Yuju");
        	else if(pl<0)printf("Yerin");
        	else printf("Shoot-off");
		}
	}
 } 

E.Bacteria Experiment

看到这题第一个想法是看能不能还原成原来的树,可是当n越大,这两种树区别似乎越小,不太好处理。这题的关键在于概率。因为每次增加的节点是等概率的,所以应该根据某种特性判断树的形状,用某一个值作为判断初始状态的标准。
可以想到,如果将某一条边切开,节点相对多的那一边获得新节点的概率更大,良性循环最后的状态更可能是相对大的那一个。
如果将每一条边都这么处理,可以看出,L的平均差异大于R(因为L每次都是1比9,R则是1:9,2:8,3:7…这样子)
这种差异将导致,最后的结果L更倾向于差异大的树,R则倾向于差异小的树。所以可以根据最后的差异来判断,他起源的更可能是那种。如何表示差异的大小呢,可以想到几种平均值,显然,想要将差异最大化的体现可以选择平方平均数(顺便把开方取平均的过程去掉,这样差异更明显,这是一种解法)
贴一波大佬题解

题意大概是,初始有1棵固定10个点的树,分图上两个类型,菊花或者链,之后对这棵树重复990次加点操作,每次加的点,都等概率地连接到已有的节点。给你最后无标号的1000个点的树的结构,让你判断初始是菊花还是链,至多1000组,95%正确率算对。
抠了一下链连出菊花或者菊花连出链的概率,990次连完之后很容易两者都有。
题解是这么写的。
https://lscct.com/challenge/2016solution.pdf

挺抽象的,前置知识Polya urn model,意思大概是初始袋子里有一些蓝球和红球,每次随机摸出1个,然后放回2个同样的球,会造成原先就多的颜色越来越多。
在这题里的体现是,对于每一条边,看他左右两块子树的大小,比如大的那块的点的代表蓝球,小的那块的点代表红球,就能套上这个model,随机选点相当于拿球。经过990次加点之后,这两块的大小差距会被放大,越来越不平衡。初始菊花图所有的边都是极度不平衡的,初始链图的边更平衡一点,我们需要权衡最终的图的平衡度来区分。题解没说具体做法,参考了别人的度量方式,一棵树的平衡度=所有子树的平衡度之和+(该树size * (n - 该树size))²。越平衡的树,就有更多的size更接近n/2,平衡度就更大,因为题面说了标号是随机打乱的,任意取一个树根即可。
代码是错的,做了两天也没能改正确,希望有大佬可以帮忙改正

#include
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
list<int> k[1111];LL ru[1111],chu[1111],ti=0,num[1111];LL g1,g2,pj,t,n;
void add(int a,int b)
{
	k[a].push_back(b);k[b].push_back(a);
}
bool vis[1111];LL ans[1111];
void dfs(int x,int fa) 
{
    ru[x]=++ti;
    num[ti]=x;list<int>::iterator it;
    for(it=k[x].begin();it!=k[x].end();++it)
	{
		if(*it!=fa)dfs(*it,x);
    }
    chu[x]=ti;
}
void phd(int x,int fa)
{
	list<int>::iterator it;int si=chu[x]-ru[x];
	ans[x]=si*si*(n-si)*(n-si);
    for(it=k[x].begin();it!=k[x].end();++it)
    {
    	if(*it==fa)continue;
    	phd(*it,x);
    	ans[x]+=ans[*it];
	}
}
int main()
{
	t=read();n=read();
	for(int i=1;i<=n-1;++i)
	{
		g1+=(n-i)*i*i*(n-i);
		g2+=(n-1)*(n-1);
	}
	pj=g1/2+g2/2;
	while(t--)
	{
		for(int i=0;i<=n;++i)k[i].clear();
		for(int i=1;i<=n-1;++i)
		{
			add(i,read());
		}
		list<int>::iterator it;
		dfs(0,-1);
		phd(0,-1);
		if(ans[0]>pj)printf("R\n");
		else printf("L\n");
	}
}

F.Anniversaries

输入一个日期问之后的2048天内有几个纪念日。
纪念日包括,100天纪念日,两个人的生日,周年纪念日
遍历一遍判断每一天是不是纪念日即可。核心是处理怎么从“今天”进入“明天”。
注意点有:
1.日期达到上限时日期归一,月份加一,月份达到上限时月份归一,年份加一。
2.注意闰二月,特别处理。

#include
#define NMAX 0x7fffffff
using namespace std;
typedef long long LL;
inline long long read()
{
	long long kk=0,f=1;
	char cc=getchar();
	while(cc<'0'||cc>'9'){if(cc=='-')f=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){kk=(kk<<1)+(kk<<3)+cc-'0';cc=getchar();}
	return kk*f;
}
int sday,smon,syear,bday,bmon,gday,gmon;int cm[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool check(int d,int m)
{
	if(d==sday&&m==smon)return 1;
	if(d==bday&&m==bmon)return 1;
	if(d==gday&&m==gmon)return 1;
	return 0;
}
void doit(int &d,int &m,int &y)
{
	bool flag=0;
	if(y%4==0)flag=1;if(y%100==0)flag=0;if(y%400==0)flag=1;//flag判断是不是闰年
	d++;
	if(m==2&&flag)//闰年的二月有29天
	{
		if(cm[m]+1<d)
		{
			m++;d=1;
		}
	}
	else if(cm[m]<d)
	{
		m++;d=1;
	}
	if(m==13)m=1,y++;
}
int main()
{
	int day=read(),mon=read(),year=read();int spe=0;
	sday=day;smon=mon;syear=year;
	bday=read();bmon=read();gday=read();gmon=read();
	for(int i=1;i<=2048;++i)
	{
		doit(day,mon,year);
		if(check(day,mon)||i%100==0)spe++;
	}
	cout<<spe;
} 

你可能感兴趣的:(训练赛)