ACM必做50题的解题模拟.docx
《ACM必做50题的解题模拟.docx》由会员分享,可在线阅读,更多相关《ACM必做50题的解题模拟.docx(31页珍藏版)》请在冰豆网上搜索。
ACM必做50题的解题模拟
POJ1029Falsecoin
Slyar:
又是假币判断问题,跟POJ1013类似,不过这个题用1013那个算法WA了...后来换了种枚举的算法才过...思路就是假币应该在每个不等式中都出现,最后只要看哪个硬币出现的次数和不等式出现的次数相同,如果这个硬币唯一,那它就是确认的假币。
#include
#include
usingnamespacestd;
constintMAX=1001;
intmain()
{
intn,k,p,total=0;
charsign;
/*记录原始数据*/
intt[MAX]={0};
/*标记硬币真假*/
intr[MAX]={0};
/*记录硬币重量*/
intw[MAX]={0};
cin>>n>>k;
while(k--)
{
/*读入原始数据*/
cin>>p;
for(inti=0;i<2*p;i++)
{
cin>>t[i];
}
cin>>sign;
/*标记肯定为真的硬币*/
if(sign=='=')
{
for(inti=0;i<2*p;i++)
{
r[t[i]]=1;
}
}
/*左轻右重*/
elseif(sign=='<')
{
total++;
for(inti=0;i
{
w[t[i]]--;
}
for(inti=p;i<2*p;i++)
{
w[t[i]]++;
}
}
/*左重右轻*/
elseif(sign=='>')
{
total++;
for(inti=0;i
{
w[t[i]]++;
}
for(inti=p;i<2*p;i++)
{
w[t[i]]--;
}
}
}
/*假币在不等式中每次都应该出现*/
intcount=0,pos=0;
for(inti=1;i<=n;i++)
{
if(r[i])
{
continue;
}
/*找出每次都出现的"假币"*/
if(w[i]==total||w[i]==-total)
{
count++;
pos=i;
}
}
poj1013CounterfeitDollar
关于称硬币的问题:
此题中赛利已经设计了正确的称量方案,保证从三组称量数据中能得到唯一的答案。
答
案可以用两个变量表示:
x¾假币的标号、w¾假币是比真币轻还是比真币重。
x共有12种
猜测;w有2种猜测。
根据赛利设计的称量方案,(x,w)的24种猜测中,只有唯一的猜测
与三组称量数据都不矛盾。
因此,如果猜测(x,w)满足下列条件,这个猜测就是要找的答
案:
l在称量结果为"even''的天平两边,没有出现x;
l如果w表示假币比真币轻,则在称量结果为"up''的天平右边一定出现x、在称量结果
为"down''的天平左边一定出现x
l如果w表示假币比真币重,则在称量结果为"up''的天平左边一定出现x、在称量结果
为"down''的天平右边一定出现x
具体实现时,要注意两点:
1)选择合适的算法
对于每一枚硬币x逐个试探:
lx比真币轻的猜测是否成立?
猜测成立则进行输出。
lx比真币重的猜测是否成立?
猜测成立则进行输出。
2)选择合适的数据结构
以字符串数组存储称量的结果。
每次称量时,天平左右最多有6枚硬币。
因此,字
符串的长度需要为7,最后一位存储字符串的结束符’\0’,便于程序代码中使用字符串
操作函数。
charleft[3][7],right[3][7],result[3][7];
#include
#include
charleft[3][7],right[3][7],result[3][5];
intislight(charx){
inti;
for(i=1;i<=3;i++)
switch(result[i][0]){
case'u':
if(strchr(right[i],x)==NULL)return0;
break;
case'e':
if(strchr(right[i],x)!
=NULL||strchr(left[i],x)!
=NULL)return0;
break;
case'd':
if(strchr(left[i],x)==NULL)return0;
break;
}
return1;
}
intisheavy(charx){
inti;
for(i=1;i<=3;i++)
switch(result[i][0]){
case'u':
if(strchr(left[i],x)==NULL)return0;
break;
case'e':
if(strchr(right[i],x)!
=NULL||strchr(left[i],x)!
=NULL)return0;
break;
case'd':
if(strchr(right[i],x)==NULL)return0;
break;
}
return1;
}
intmain(){
inti,j,n;
charc;
scanf("%d",&n);
while(n--){
for(i=1;i<=3;i++)
scanf("%s%s%s",left[i],right[i],result[i]);
for(c='A';c<='L';c++){
if(islight(c)){
printf("%cisthecounterfeitcoinanditislight.\n",c);
break;
}
if(isheavy(c)){
printf("%cisthecounterfeitcoinanditisheavy.\n",c);
break;
}
}
}
return0;
}
/*假币唯一则输出*/
if(count!
=1)
{
cout<<0<}
else
{
cout<}
//system("pause");
return0;
}
poj1083MovingTables
就是把这400个房间分成200分,1-2,3-4,。
。
。
每次移动时,都要把经过的“份”算上,这样最后找份中最大的那个数即可
#include
#include
#include
#defineSIZE220
usingnamespacestd;
voidswapnum(int&a,int&b);
intgetindex(intk);
intmain()
{
intt,n;
cin>>t;
while(t--){
cin>>n;
intx,y;
intcross[SIZE];
memset(cross,0,sizeof(cross));
for(inti=0;icin>>x>>y;
intstart,end;
if(x>y){
swapnum(x,y);
}
start=getindex(x);
end=getindex(y);
for(inti=start;i<=end;++i){
cross[i]++;
}
}
intmaxnum=INT_MIN;
for(inti=0;imaxnum=maxnum>cross[i]?
maxnum:
cross[i];
}
cout<}
return0;
}
voidswapnum(int&a,int&b)
{
inttemp=a;
a=b;
b=temp;
}
intgetindex(intk)
{
if(k%2==0){
returnk/2;
}
elseif(k%2==1){
returnk/2+1;
}
}
poj2028WhenCanWeMeet?
题意:
ICPC委员会要安排一个会议,但是成员们都太忙了,所以很难安排,所以要我们编程找个最合适的日子让更多的人来参加这个会议。
一共有N个人,至少要Q个人参加,第i个人有mi天是有空的,分别是date1,date2…..datem,表示明天开始的第datei天,比如date1为1表示明天有空,date2为2表示后天有空,……要输出最好的那天,要求是参加的人尽可能多,如果用相同人数的要尽量靠前。
思路:
从第1到max开始枚举,看多少人在第i天有空,如果用>=q的人有空的话,把人数跟i记录下来,放在结构体数组里,最后按人数从大到小排序,如果最大的那个的人数Max是这么多人的有空的日子里最晚的那天,
#include
#include
#include
#defineN55
#defineM105
typedefstruct
{
intindex;
intcount;
}item;
itemitems[M];
intcmp(constvoid*p,constvoid*q)
{
return(*(int*)p)-(*(int*)q);
}
intcmp2(constvoid*p,constvoid*q)
{
item*a=(item*)p;
item*b=(item*)q;
if(a->count>b->count)
return-1;
elseif(a->countcount)
return1;
else
{
returna->index>b->index?
1:
-1;
}
}
intmain()
{
inti,j,k;
intsize,quorum,dateNum,result,count,max;
intdates[N][M];
int*find;
while(scanf("%d%d",&size,&quorum)&&size&&quorum)
{
memset(dates,0,sizeof(dates));
memset(items,0,sizeof(items));
max=0;
for(i=0;i{
scanf("%d",&dates[i][0]);
for(j=1;j<=dates[i][0];j++)
{
scanf("%d",&dates[i][j]);
if(maxmax=dates[i][j];
}
}
for(i=1,k=0;i<=max;i++)
{
for(j=0,count=0;j{
find=(int*)bsearch(&i,dates[j]+1,dates[j][0],sizeof(dates[j][1]),cmp);
if(find!
=NULL)
{
count++;
}
}
if(count>=quorum)
{
items[k].count=count;
items[k].index=i;
k++;
}
if(count==size)
break;
}
qsort(items,k,sizeof(items[0]),cmp2);
if(items[0].count>=quorum)
printf("%d\n",items[0].index);
elseprintf("0\n");
}
return0;
}
poj2234MatchesGame异或的问题
不是很理解,没办法强记策略
尼姆博奕(NimmGame):
有n堆各若干个物品,两个人轮流从某一堆取任意多(或者最多m个,只需把每堆%m)的物品,规定每次至少取一个,多者不限,最后取光者得胜。
把每堆数量求异或a1^a2^...^ai'^...^an,结果为零。
则先手必输,否则必赢。
#include
usingnamespacestd;
intmain(){
intn,ans,temp;
while(scanf("%d",&n)!
=EOF)
{
scanf("%d",&ans);
n--;
while(n--)
{
scanf("%d",&temp);
ans^=temp;
}
if(ans)
printf("Yes\n");
else
printf("No\n");
}
return0;
}
poj1067取石子游戏
有两堆石头,一次可以取走其中一堆的任意个石头或者在两堆中取走相同数量的石头。
现在两个人相互比赛,谁最后没有石头取则视为输家。
输入两堆石头的数目让你判断,若是双方都按照最优原则取,谁会是赢家。
开始以为是一个贪心算法。
实际上错了。
这个题目主要讲的是一个数学定理,说白了就是一个贝蒂定理。
描述如下:
1.alpha,beta>0
2.1/alpha+1/beta=1
则[alpha*n],[beta*n]能够成正整数集的一个划分。
这个题目中:
alpha=(1+sqrt(5))/2;
beta=(3+sqrt(5))/2;
代码写的不算漂亮,有几处细节:
1:
sprt函数的输入要么是浮点型要么是double型,所以输入要写成5.0,而自己长期以来用整形数习惯了。
这个地方出现了编译错误。
2:
cmath库中还有ceil和swap函数,还有点意外,之前以为要自己写。
3:
Rbig==(int)beta*n;
Rsmall=(int)alpha*n;之前我是这样写的,但是结果却错了。
原因是这样写会将alpha,beta强制转换。
而不是将整个相乘的结果转换成整数。
#include"iostream"
#include
usingnamespacestd;
intmain(intargc,char*argv[])
{
intn;
intmax,min;
intRbig,Rsmall;
doublealpha=(1+sqrt(5.0))/2;
doublebeta=(3+sqrt(5.0))/2;
intm=8;
while(cin>>max>>min)
{
if(maxswap(max,min);
n=ceil(max/beta);
Rbig=/*(int)*/beta*n;
Rsmall=/*(int)*/alpha*n;
if(Rbig==max&&Rsmall==min)
{
cout<<0<}
else
cout<<1<}
return0;
}
POJ1012Joseph
约瑟夫问题都知道了,这个题就是给出一个k,总人数n等于2k,让你找到一个报数m,使得后k个人先出列...
反正k才到14,暴力枚举就行了,不过由于数据比较多,需要开一个数组保存一下才不会超时...
#include
#include
usingnamespacestd;
intwork(int,int);
intmain()
{
inti,k;
vectorarray(14);
for(k=1;k<14;k++)
{
for(i=k+1;;i+=(k+1))
{
if(work(k,i))
{
array[k]=i;
break;
}
elseif(work(k,i+1))
{
array[k]=i+1;
break;
}
}
}
while
(1)
{
cin>>k;
if(k==0)break;
cout<}
return0;
}
intwork(intk,intm)
{
inti=0,len=2*k;
while(len>k)
{
i=(i+m-1)%len;
if(ilen--;
}
return1;
}
POJ1026Cipher
说下题目大意。
首先输入长度为n的数字串,设为key[i],表示第i个字符置换一次后跑到key[i]的位置。
然后输入若干字符序列,设字符序列为Src(若长度不足n则后面用空格补齐),求Src中的每个字符进行m次置换后的字符序列Dst。
加密方式如题中所给:
12345678910
45372816109
对于第一个字符,加密3次的结果如下:
1-->4-->7-->1
对于第二个字符,加密2次的结果如下:
2-->5-->2
可以看到,加密一定次数后,结果会构成一个循环,如果我们求出这个循环周期,那么加密的次数就可以使用取余运算进行缩减了。
这次没有使用C++来做,因为gets和puts很好用,而String在这里发挥不出什么作用...
#include
#include
#include
#defineMAX202
intkey[MAX],t[MAX];
/*求解置换周期*/
voidget_time(intn)
{
inti,j,count;
for(i=1;i<=n;i++)
{
j=key[i];
count=1;
/*直到轮回*/
while(i!
=j)
{
count++;
j=key[j];
}
t[i]=count;
}
}
intmain()
{
inti,j,m,n,len;
charsrc[MAX],dst[MAX];
while
(1)
{
scanf("%d",&n);
if(n==0)break;
for(i=1;i<=n;i++)
{
scanf("%d",&key[i]);
}
/*初始化周期数组*/
memset(t,0,sizeof(t));
get_time(n);
while
(1)
{
scanf("%d",&m);
if(m==0)break;
getchar();
/*读入输入串*/
gets(src+1);
/*补全输入串*/
for(i=strlen(src+1)+1;i<=n;i++)
{
src[i]='';
}
src[n+1]=0;
/*求解输出串*/
for(i=1;i<=n;i++)
{
intpos=i;
for(j=0;j{
pos=key[pos];
}
dst[pos]=src[i];
}
dst[n+1]=0;
puts(dst+1);
}
printf("\n");
}
//system("pause");
return0;
}
POJ1068Parencodings
这道题目是一道模拟题。
P-sequence表示第i个‘)’之前有几个‘(’,W-sequence表示第i个‘()’包含几对‘()’,要求对应P的W。
题目中没有要输出S,故‘(’‘)’可以分别用0,1来代替。
根据P可以轻易知道‘)’的位置:
location=p[i]+i。
用value记录w[i]的值,用flag记录括号是否成对。
#include
#include
#include
intt,n;
ints[41],p[21],w[21];
intmain()
{
scanf("%d",&t);
while(t--){
scanf("%d",&n);
inttemp,flag,value;
memset(s,0,sizeof(s));
for(inti=0;iscanf("%d",&p[i]);
temp=p[i]+i;
s[temp]=1;
for(intk=temp-1,value=1,flag=0;k>=0;--k){
if(s[k]==0&&flag==0){
w[i]=value;
break;
}
elseif(s[k]==1){
value++;
flag++;
}
elseif(s[k]