完成了在ZOJ上的处女题,小兴奋,在此记录一下:
一、工程代码加上算法设计注释
--------------------------------------------------crash_ballon.h----------------------------------------------
#ifndef JLU_CCST_GDC_CRASH_BALLON_H
#define JLU_CCST_GDC_CRASH_BALLON_H
extern int whoIsWinner(int player1,int player2,int max);
extern void testWhoIsWinner();
#endif//JLU_CCST_GDC_CRASH_BALLON_H
--------------------------------------------------crash_ballon.cpp----------------------------------------------
/**
题目来源:浙大ACM在线测试——ZOJ,题目编号1003,题名"Crashing Balloon"
URL:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1003
Author:hellogdc<gdcjlu@163.com>
Finish Time:2013.12.31
*/
/**
算法设计:
1. 求出每个数所有的因式分解序列
递归求解——一个数的因式分解序列可以由其因子的因式分解序列构成
2. 检查2个队列的队列的“相容性”
遍历第1个队列的队列,取出每个队列中的元素,做如下处理:
将第2个队列的队列中包含该元素的序列“删除”。
如果遍历完该队列所有元素后,第2个队列中仍然存在一些队列,说明是可行的;否则继续处理下一个队列。
*/
#include <iostream>
#include <list>
using namespace std;
typedef list<int> ListInt;
/**
对n进行因式分解,并输出所有可能的序列,可能会出现重复的序列,但如果按出现位置来考虑的话,那么
每个序列都是唯一的。如果不考虑位置的话,那么每个序列会出现m次(m值是序列长度的阶乘)。
另外,任何一个因式分解序列中不允许出现重复的因子。
另外,该函数不将n本身作为分解序列输出。
一个问题:如果N很大的话,那么意味着要申请一个很大的数组,此时再用数组模拟栈就不合适了,应该真正的
建立一个栈,这样才能使栈的规模大小为所有的节点个数。
返回:所有的序列数目
*/
const int N=10000;
int stack[N];//值为-1,说明该点未在栈中。>=0,则说明该点在栈中。
int top=0;//指向栈顶
static int decompose(int n){
int count=0;
for(int i=2;i<n;i++){
bool flag=true;
if(n%i==0){
//保证这2个要入栈的点不是同一个点
if( i==(n/i) )
flag=false;
//检查下面2个要入栈的点是否已经在栈中出现,如出现,则该分解不合法,返回。
int head=top;
while(flag&&stack[head]>=0){
if(head==i||head==n/i){
flag=false;
break;
}
head=stack[head];
}
if(!flag)
continue;
//入栈——树边
stack[i]=top;
top=i;
count+=decompose(n/i);
//入栈——树节点
stack[n/i]=top;
top=n/i;
//输出栈内容
head=top;
while(stack[head]>=0){
cout<<head<<'\t';
head=stack[head];
}
cout<<endl;
//2次弹栈——弹出压入栈的树边和树节点
int tmp=top;
top=stack[top];
stack[tmp]=-1;
tmp=top;
top=stack[top];
stack[tmp]=-1;
count++;
}
}
return count;
}
/**
基于decompose()函数遇到的问题——不能处理数据值很大的问题,利用list来模拟“栈”。
另外,加一个参数max,用来设置因子的上限。如果因子分解序列中某个因子大于这个
上限,那么这个序列就可以舍去了。
返回:所有的序列数目
*/
ListInt listForStack;
static int decomposeOptimizationWithList(int n,int max){
int count=0;
ListInt::iterator it;
for(int i=2;i<n;i++){
bool flag=true;
if(n%i==0){
//保证每个因子比max小
if(i>max)
flag=false;
//保证这2个要入栈的点不是同一个点
if( i==(n/i) )
flag=false;
//检查下面2个要入栈的点是否已经在栈中出现,如出现,则该分解不合法,返回。
for(it=listForStack.begin();it!=listForStack.end();it++){
if(*it==i||*it==n/i){
flag=false;
break;
}
}
if(!flag)
continue;
//入栈——树边
listForStack.push_front(i);
count+=decomposeOptimizationWithList(n/i,max);
if( (n/i) <=max ){//如果该“树节点”因子比max小,则入栈,并且输出
//入栈——树节点
listForStack.push_front(n/i);
//输出栈内容
for(it=listForStack.begin();it!=listForStack.end();it++)
cout<<*it<<'\t';
cout<<endl;
//2次弹栈——弹出压入栈的树边和树节点
listForStack.pop_front();
count++;
}
listForStack.pop_front();
}
}
return count;
}
/**
decompose()和decomposeOptimizationWithList()都会存在一个问题,那就是因式分解序列的重复
出现(不考虑因子出现的次序)。
考虑到每个序列以因子的各种顺序形式出现,那么如果我想得到唯一的一个序列,只要让因子以递增
顺序排列即可。
整个序列的递增顺序可以转换为每次加入元素时,都要确保该元素比“栈”顶元素小
*/
ListInt listForStackUnique;
static int decomposeUnique(int n,int max){
int count=0;
ListInt::iterator it;
for(int i=2;i<n;i++){
bool flag=true;
if(n%i==0){
//保证每个因子比max小
if(i>max)
flag=false;
//保证这2个要入栈的点不是同一个点
if( i==(n/i) )
flag=false;
//检查下面2个要入栈的点是否已经在栈中出现,如出现,则该分解不合法,返回。
for(it=listForStackUnique.begin();it!=listForStackUnique.end();it++){
if(*it==i||*it==n/i){
flag=false;
break;
}
}
if(!flag)
continue;
//入栈——树边
//每次入栈前,做一次大小判断
if( (!listForStackUnique.empty()) && i>listForStackUnique.front() )
continue;
listForStackUnique.push_front(i);
count+=decomposeUnique(n/i,max);
if( (n/i) <=max && //如果该“树节点”因子比max小,则入栈,并且输出
(listForStackUnique.empty()||(n/i)<listForStackUnique.front())){//每次入栈前,做一次大小判断
//入栈——树节点
listForStackUnique.push_front(n/i);
//输出栈内容
for(it=listForStackUnique.begin();it!=listForStackUnique.end();it++)
cout<<*it<<'\t';
cout<<endl;
//2次弹栈——弹出压入栈的树边和树节点
listForStackUnique.pop_front();
count++;
}
listForStackUnique.pop_front();
}
}
return count;
}
/**
基于decomposeUnique()函数,获取数的因式分解序列,不过这次不是输出,而是保存到队列中
*/
ListInt listForStackUniqueForGetValue;
static int getValueDecompisitionList(int n,int max,list<ListInt*>* deList){
int count=0;
ListInt::iterator it;
for(int i=2;i<n;i++){
bool flag=true;
if(n%i==0){
//保证每个因子比max小
if(i>max)
flag=false;
//保证这2个要入栈的点不是同一个点
if( i==(n/i) )
flag=false;
//检查下面2个要入栈的点是否已经在栈中出现,如出现,则该分解不合法,返回。
for(it=listForStackUniqueForGetValue.begin();it!=listForStackUniqueForGetValue.end();it++){
if(*it==i||*it==n/i){
flag=false;
break;
}
}
if(!flag)
continue;
//入栈——树边
//每次入栈前,做一次大小判断
if( (!listForStackUniqueForGetValue.empty()) && i>listForStackUniqueForGetValue.front() )
continue;
listForStackUniqueForGetValue.push_front(i);
count+=getValueDecompisitionList(n/i,max,deList);
if( (n/i) <=max && //如果该“树节点”因子比max小,则入栈,并且输出
(listForStackUniqueForGetValue.empty()||(n/i)<listForStackUniqueForGetValue.front())){//每次入栈前,做一次大小判断
//入栈——树节点
listForStackUniqueForGetValue.push_front(n/i);
//将队列内容保存到deList中
ListInt* tmpList=new ListInt(listForStackUniqueForGetValue.size());
ListInt::iterator tmpIt=tmpList->begin();
for(it=listForStackUniqueForGetValue.begin();it!=listForStackUniqueForGetValue.end();it++,tmpIt++)
*tmpIt=*it;
deList->push_front(tmpList);
//弹栈——弹出压入栈的树节点
listForStackUniqueForGetValue.pop_front();
count++;
}
//弹栈——弹出压入栈的树边点
listForStackUniqueForGetValue.pop_front();
}
}
return count;
}
/**
比较二者之间谁能获胜。
处理:
1. 如果二者数值相等,则无法判断谁为赢家,返回-1
2. 如果二者均不能在规定的最大值因子下获得因式分解序列,则值较大的为赢家
3. 如果一人可以得到合法的因式分解序列,而另一人没得到,则赢家为拥有合法序列的玩家
4. 如果二者均有合法序列,则按照低分者计算正确,来分析高分者的因式分解序列是否合理
如何判断:遍历低分者的所有因式分解序列,对该序列中的每个元素做如下处理:
4.1 遍历高分者的所有因式分解序列,将包含该元素的所有序列删除(其实是放进另一个备份队列中)。
4.2 遍历完后,如果高分者队列不为空,说明存在一个合法的序列与该低分者的序列对应,则说明高分者为赢家;
否则,根据备份队列还原高分者队列内容,继续处理低分者队列中的下一个序列。
返回:-1表示无赢家;否则,将返回赢家的数值
*/
int whoIsWinner(int player1,int player2,int max){
int winner=-1;
bool isWinnerFound=false;
int lowPlayer=player1,highPlayer=player2;
if(player1==player2)//无赢家
return -1;
if(player1>player2){
lowPlayer=player2;
highPlayer=player1;
}
list<ListInt*>* lowList=new list<ListInt*>();
list<ListInt*>* highList=new list<ListInt*>();
list<ListInt*>* highListBack1=new list<ListInt*>();//存放高分者队列中被移除的元素
list<ListInt*>* highListBack2=new list<ListInt*>();//存放高分者队列中剩下的元素
ListInt* valueSelf=NULL;
int count=-1;
count=getValueDecompisitionList(lowPlayer,max,lowList);
if(lowPlayer<=max){
valueSelf=new ListInt(1,lowPlayer);
lowList->push_front(valueSelf);
count++;
}
//检查低分者是否计算错误
if(count<=0)
lowPlayer=highPlayer;//说明该低分者不可能为赢家,则赢家只能为高分者
count=getValueDecompisitionList(highPlayer,max,highList);
if(highPlayer<=max){
valueSelf=new ListInt(1,highPlayer);
highList->push_front(valueSelf);
count++;
}
//检查高分者是否撒谎
if(count<=0){
winner= lowPlayer;
isWinnerFound=true;
}
if(!isWinnerFound){
//因为低分者都说真话,那么基于低分者的因式分解序列队列,对高分者的因式分解队列进行检查
for(list<ListInt*>::iterator it=lowList->begin();it!=lowList->end()&&(!isWinnerFound);it++){
//依据低分者队列的每个合法序列的每个元素,来剔除掉高分者队列中的相应序列
for(list<int>::iterator itt=(*it)->begin();itt!=(*it)->end()&&(!highList->empty());itt++){
while(!highList->empty()){
ListInt* top=highList->front();
highList->pop_front();
//检查该序列中是否包含低分者序列中的值
bool isRemove=false;
for(list<int>::iterator highIt=top->begin();highIt!=top->end();highIt++){
if(*itt==*highIt){
isRemove=true;
break;
}
}
if(isRemove)
highListBack1->push_front(top);
else
highListBack2->push_front(top);
}
//切换队列指针
list<ListInt*>* tmp=highListBack2;
highListBack2=highList;//该队列在每次循环开始时,都为空队列
highList=tmp;
}
//检查高分者队列中是否还存在可行的因式分解序列
if(!highList->empty()){
isWinnerFound=true;
winner=highPlayer;
break;
}else{
//还原高分者队列,继续处理低分者队列中的下一个序列
list<ListInt*>* tmp=highListBack1;
highListBack1=highList;
highList=tmp;
}
}
}
if(!isWinnerFound)//说明对于低分者的所有合法序列,高分者中都不存在对应的合法序列,说明高分者在撒谎
winner=lowPlayer;
//清空队列
while(!highList->empty()){
ListInt* top=highList->front();
highList->pop_front();
delete top;
}
while(!lowList->empty()){
ListInt* top=lowList->front();
lowList->pop_front();
delete top;
}
while(!highListBack1->empty()){
ListInt* top=highListBack1->front();
highListBack1->pop_front();
delete top;
}
while(!highListBack2->empty()){
ListInt* top=highListBack2->front();
highListBack2->pop_front();
delete top;
}
delete highList;
delete lowList;
delete highListBack1;
delete highListBack2;
return winner;
}
static void testDecompseFunc(){
for(int i=0;i<N;i++)
stack[i]=-1;
int count=0;
cout<<"**********************decompose************************************"<<endl;
count=decompose(500);
cout<<"Count="<<count<<endl;
cout<<"**********************decomposeOptimizationWithList*****************"<<endl;
count=decomposeOptimizationWithList(500,100);
cout<<"Count="<<count<<endl;
cout<<"**********************decomposeOptimizationWithList******************"<<endl;
count=decomposeOptimizationWithList(500,1000);
cout<<"Count="<<count<<endl;
cout<<"*********************decomposeUnique******************"<<endl;
count=decomposeUnique(42,1000);
cout<<"Count="<<count<<endl;
cout<<"*******************getValueDecompisitionList**********************"<<endl;
list<ListInt*> deList;
count=getValueDecompisitionList(42,1000,&deList);
for(list<ListInt*>::iterator it=deList.begin();it!=deList.end();it++){
for(ListInt::iterator tmpIt=(*it)->begin();tmpIt!=(*it)->end();tmpIt++)
cout<<*tmpIt<<'\t';
cout<<endl;
delete (*it);
}
cout<<"Count="<<count<<endl;
}
void testWhoIsWinner(){
int player1=343,player2=49;
int winner;
cout<<"player1="<<player1<<endl;
cout<<"player2="<<player2<<endl;
winner=whoIsWinner(player1,player2,100);
if(winner==-1)
cout<<"the challenge would not be upheld"<<endl;
else
cout<<"winner="<<winner<<endl;
cout<<"**************************************************************"<<endl;
player1=3559,player2=610;
cout<<"player1="<<player1<<endl;
cout<<"player2="<<player2<<endl;
winner=whoIsWinner(player1,player2,100);
if(winner==-1)
cout<<"the challenge would not be upheld"<<endl;
else
cout<<"winner="<<winner<<endl;
cout<<"**************************************************************"<<endl;
player1=62,player2=36;
cout<<"player1="<<player1<<endl;
cout<<"player2="<<player2<<endl;
winner=whoIsWinner(player1,player2,100);
if(winner==-1)
cout<<"the challenge would not be upheld"<<endl;
else
cout<<"winner="<<winner<<endl;
cout<<"**************************************************************"<<endl;
player1=10001,player2=10003;
cout<<"player1="<<player1<<endl;
cout<<"player2="<<player2<<endl;
winner=whoIsWinner(player1,player2,100);
if(winner==-1)
cout<<"the challenge would not be upheld"<<endl;
else
cout<<"winner="<<winner<<endl;
}
--------------------------------------------------main.cpp----------------------------------------------
#if 0
#include "crash_ballon.h"
int main(){
testWhoIsWinner();
return 0;
}
#endif
二、提交并被ZOJ成功接受的代码——算法核心代码
--------------------------------------------------submmit_main.cpp----------------------------------------------
#include <iostream>
#include <list>
using namespace std;
typedef list<int> ListInt;
static ListInt listForStackUniqueForGetValue;
static int getValueDecompisitionList(int n,int max,list<ListInt*>* deList){
int count=0;
ListInt::iterator it;
for(int i=2;i<n;i++){
bool flag=true;
if(n%i==0){
if(i>max)
flag=false;
if( i==(n/i) )
flag=false;
for(it=listForStackUniqueForGetValue.begin();it!=listForStackUniqueForGetValue.end();it++){
if(*it==i||*it==n/i){
flag=false;
break;
}
}
if(!flag)
continue;
if( (!listForStackUniqueForGetValue.empty()) && i>listForStackUniqueForGetValue.front() )
continue;
listForStackUniqueForGetValue.push_front(i);
count+=getValueDecompisitionList(n/i,max,deList);
if( (n/i) <=max &&
(listForStackUniqueForGetValue.empty()||(n/i)<listForStackUniqueForGetValue.front())){
listForStackUniqueForGetValue.push_front(n/i);
ListInt* tmpList=new ListInt(listForStackUniqueForGetValue.size());
ListInt::iterator tmpIt=tmpList->begin();
for(it=listForStackUniqueForGetValue.begin();it!=listForStackUniqueForGetValue.end();it++,tmpIt++)
*tmpIt=*it;
deList->push_front(tmpList);
listForStackUniqueForGetValue.pop_front();
count++;
}
listForStackUniqueForGetValue.pop_front();
}
}
return count;
}
static int whoIsWinner(int player1,int player2,int max){
int winner=-1;
bool isWinnerFound=false;
int lowPlayer=player1,highPlayer=player2;
if(player1==player2)
return player1;
if(player1>player2){
lowPlayer=player2;
highPlayer=player1;
}
list<ListInt*>* lowList=new list<ListInt*>();
list<ListInt*>* highList=new list<ListInt*>();
list<ListInt*>* highListBack1=new list<ListInt*>();
list<ListInt*>* highListBack2=new list<ListInt*>();
ListInt* valueSelf=NULL;
int count=-1;
count=getValueDecompisitionList(lowPlayer,max,lowList);
if(lowPlayer<=max){
valueSelf=new ListInt(1,lowPlayer);
lowList->push_front(valueSelf);
count++;
}
if(count<=0)
lowPlayer=highPlayer;
count=getValueDecompisitionList(highPlayer,max,highList);
if(highPlayer<=max){
valueSelf=new ListInt(1,highPlayer);
highList->push_front(valueSelf);
count++;
}
if(count<=0){
winner= lowPlayer;
isWinnerFound=true;
}
if(!isWinnerFound){
for(list<ListInt*>::iterator it=lowList->begin();it!=lowList->end()&&(!isWinnerFound);it++){
for(list<int>::iterator itt=(*it)->begin();itt!=(*it)->end()&&(!highList->empty());itt++){
while(!highList->empty()){
ListInt* top=highList->front();
highList->pop_front();
bool isRemove=false;
for(list<int>::iterator highIt=top->begin();highIt!=top->end();highIt++){
if(*itt==*highIt){
isRemove=true;
break;
}
}
if(isRemove)
highListBack1->push_front(top);
else
highListBack2->push_front(top);
}
list<ListInt*>* tmp=highListBack2;
highListBack2=highList;
highList=tmp;
}
if(!highList->empty()){
isWinnerFound=true;
winner=highPlayer;
break;
}else{
list<ListInt*>* tmp=highListBack1;
highListBack1=highList;
highList=tmp;
}
}
}
if(!isWinnerFound)
winner=lowPlayer;
while(!highList->empty()){
ListInt* top=highList->front();
highList->pop_front();
delete top;
}
while(!lowList->empty()){
ListInt* top=lowList->front();
lowList->pop_front();
delete top;
}
while(!highListBack1->empty()){
ListInt* top=highListBack1->front();
highListBack1->pop_front();
delete top;
}
while(!highListBack2->empty()){
ListInt* top=highListBack2->front();
highListBack2->pop_front();
delete top;
}
delete highList;
delete lowList;
delete highListBack1;
delete highListBack2;
return winner;
}
#if 1
int main(){
int player1=343,player2=49;
int winner;
while( cin>>player1>>player2 ){
winner=whoIsWinner(player1,player2,100);
cout<<winner<<endl;
}
return 0;
}
#endif
注:刚开始提交的时候,ZOJ提示“Wrong Answer”,上网上看了别人实现该题的代码,发现自己在2个数值相等的情况下,没处理好。在这种情况下,直接将值输出就行,而我则是返回-1,说明无法进行比较,结果是多次一举。将这个处理改完之后,提交,就被成功接受了。
三、网上别人的代码
原文地址:http://blog.csdn.net/jayfer/article/details/1783649
1003问题,本来以为要用数论的某些东西来解,但是想了好久都没有找到一个好的算法。惭愧本人能力有限,于是在网上找到了些解法。
以下是网上搜到的解法:
原来帖在ZJU的讨论区里了 今天再往这里帖一次 请大家提出意见:)
(*)ZJU 1003 - Crashing Balloon - 00:00.18 688K
http://acm.zju.edu.cn/show_problem.php?pid=1003
WA 2次 AC 1次
这道题我初看的时候想找数学方法,不过看来行不通。用回溯搜索就可以了。
先将题目判断胜负的标准列出来:
1.如果肯定获胜者说谎而挑战者说真话,则挑战者成功。
2.如果获胜者和挑战者都有可能说了真话,则挑战者失败。
3.如果挑战者说谎,挑战者失败。
列表就是:
获胜者 挑战者 胜方
假 真 挑战者
真 真 获胜者
假 获胜者
作法1:那我们只需要先判断挑战者的分数是否能正确分解,不能则输出获胜者的分数。能则进行搜索,先将获胜者的分数进行分解,将用过的因数标记,分解成某一形式后检查挑战者的分数是否能分解为剩下的因数之积。能,说明获胜者有说真话的情况,输出获胜者的分数。一直不能或是获胜者的分数无法分解,说明获胜者说假话,输出挑战者的分数。(见Starfish的程序)
作法2:用回溯搜索,从2开始一直搜索因数到100。设获胜者的分数为m,挑战者的分数为n(m>n),当前搜索到的因数为p,flag1为是否两人分数能分解成一合法形式,flag2为挑战者的分数是否符合要求。搜索函数为f(m, n, p),初始时flag1 = flag2 = FALSE。由于每个因数只能出现一次,所以:如果p|m,则f(m DIV p, n, p+1),如果p|n,则f(m, n DIV p, p+1)。还有可能不分解出因数p,所以当p<m或p<n时,f(m, n, p+1)。当搜索到m=1且n=1时,说明存在一种没有出现相同因数的分解,设flag1为TRUE。如果发现n=1,则挑战者的分数可以分解,符合要求,设为TRUE。最后的分析表格同上表。优化:flag1和flag2的初始状态都是FALSE,我们由上面的分析可以发现,flag1只要变成TRUE,最后的答案也就定下来了,于是,当flag1发生改变时就可以退出搜索了。(见Bamboo或我的程序)
错误总结:第一次错在“如果搜索到m和n在2-100之间且m和n相等时,说明存在出现相同因数的情况,设flag2为TRUE”,反例:12 = 2*6 = 3*4。由此发现,是否出现相同因数并不好计算。还有读题目的时候也没理解好,第3点错了。
另:附有Bamboo,Starfish的代码。我和代码和Bamboo的一样。还有Idler的经验:他的程序和我的几乎一样,但是速度飞快,从大到小搜一般都比较快!最好是看Starfish的代码。
这里附上找到的代码:
int flag1,flag2;
// flag1为是否两人分数能分解成一合法形式,flag2为挑战者的分数是否符合要求
void dfs ( int n , int m , int fac )
{
if(flag1== 1 )
return ;
if(m==1 && n==1 )
{
flag1=1;
flag2=1;
return;
}
if(m==1)
flag2=1;
if(fac<2 )
return;
if(n%fac==0)
dfs(n/fac,m,fac-1);
if(m%fac== 0 )
dfs(n,m/fac,fac-1);
dfs(n,m,fac-1);
}
int main()
{
int tmp,n,m;
while(scanf("%d %d",&n,&m)!=EOF)
{
if(m>n)
{
tmp=m;
m=n;
n=tmp;
}
flag1=flag2=0;
dfs(n,m,100);
if(flag1||!flag2)
printf("%d ",n);
else if(flag2==1&&flag1==0)
printf("%d ",m);
}
return 0;
}
以上是非常简单的算法。用的是就是回溯搜索。
我当时看懂后,感觉豁然一亮!
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1584638