您的位置:首页 > 其它

usaco chapter1:4,5 (2)

2011-09-21 06:00 260 查看
The clocks

题目要求按给的9种操作,将所给状态的钟全部调为12点。

毋庸置疑地用bfs,貌似是很简单啊。第一次提交后超时了,样例都过不了。

第一次的方案:

从初始状态开始,每进行一次操作就将新生成的状态存入队列......也就是说这是赤裸裸的bfs;

因为操作的顺序与结果无关,所以这样做会重复搜索很多状态,比如 1->2 ,与2->1就重复了......

所以要想个办法判重,另外还有更加重要的一点,每种操作最多只能用3次,因为用4次相当于整整转了一圈。

发现这个条件后,解答树的深度就被限定死了,不过要判重就很麻烦(虽然不判重,也可以过),我们可以换种方式表达。

每种操作次数为0,1,2,3,所以可以用9位4进制数来表示所有操作......

关键代码:

typedef struct clock

{

int time[9];

}clock;

clock loop(int i,int times,clock root)
{
int j,k;
clock temp;
temp=root;
for(j=0;j<times;j++)
{
for(k=0;k<9;k++)
temp.time[k]=(operatin[i][k]+temp.time[k])%4;
}
return temp;
}
clock operate(int z,clock root)
{
int i;
char* list;
clock temp;
temp=root;
// strcpy(to_base_4(z),list); //把i转换成4进制,存入list,根据list来操作root;
list=to_base_4(z);
for(i=0;i<strlen(list);i++)
{
temp=loop(i,list[i]-'0',temp); //对root执行i操作,执行 list[i] 次;
}
free(list);
return temp;
}

所以说:开始写搜索前,一定要分析这样写的最大运行量,可以不优化,但不可以不考虑优化的可能性与必要性(若是必须的当然还是要优化....囧)。

Arithmetical progressions(等差数列)

要你给出在给定范围内的双平方(bisquares)AP。

第一行: N(3<= N<=25),要找的等差数列的长度。

第二行: M(1<= M<=250),搜索双平方数的上界0 <= p,q <= M。

这道题貌似还算顺利,先生成一个范围内的双平方数表,好方便判断。因为p,q有上界m,所以有很多地方可以减枝,比如说a+(n-1)*b <=2*m*m , 这可以限制a和b的枚举,还可以从后往前枚举,如果后面判断成功了,而前面出现了同样公差的数列,那么对它的判断肯定也为真,可以跳过()。

nocow讨论了枚举首项a和公差b的顺序,有人说先枚举a,不过我觉得都一样(当然可能是程序结构的不同导致的分歧,这不是绝对的)

for(b=1;b<=2*m*m/(n-1)+1;b++) //枚举公差;
for(a=0;a+(n-1)*b<=2*m*m;a++)


若先枚举a

for(a=0;a+n-1<=2*m*m;a++)

for(b=1;a+(n-1)*b<=2*m*m;b++)

虽然a的循环边界变大了,但a越大,b的枚举次数就要越小。

Mother's milk( 妈妈的奶?....让人浮想联翩啊&@¥@)

这道题和倒水问题如出一辙,倒水是动规求有向图的最短路径,这个初看却没有动态规划目标,要求遍历所有路径,或许可以用bfs遍历,nocow讨论了许多优化方案,但是我有更好的办法。

既然一次不行,可以来多次,每次规定一个目标状态,再判定能否到达。

代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct node
{
int am[3];
}node;
node q[10000];

int min(int x,int y)
{
if(x>y)
return y;
else
return x;
}

int cmp(const void* x,const void* y)
{
return *(int*)x - *(int*)y;
}
int hash[20],cup[3]; //vis用来bfs判重 , hash记录曾经出现过的状态;
int bfs(int a,int b,int c,int aim)
{
int front=0,rear=0,pour,i,j,vis[20][20];
node t;
memset(q,0,sizeof(q));
q[rear].am[0]=a;
q[rear].am[1]=b;
q[rear++].am[2]=c;
if(c==aim)return 1;
memset(vis,0,sizeof(vis));
while(front<rear)
{
if(q[front].am[2]==aim&&q[front].am[0]==0)return 1;
for(i=2;i>=0;i--) //i表示倒水的杯子,j表示接水的杯子,生成新状态;
for(j=2;j>=0;j--)
if(j!=i)
{
t=q[front];
pour=min(cup[j]-t.am[j],t.am[i]);
t.am[i]-=pour;
t.am[j]+=pour;
if(!vis[t.am[2]][t.am[0]]) //若该状态没有出现过;
{
q[rear++]=t;
vis[t.am[2]][t.am[0]]=1;
if(t.am [0]==0)hash[t.am[2]]=1; //记录新状态的c的状态
}
}
front++;
}
return 0;
}
int main()
{
FILE *fin,*fout;
fin=fopen("milk3.in","r");
fout=fopen("milk3.out","w");
int i,t=0,flag=0,path[10000];
// node temp;
memset(hash,0,sizeof(hash));
// scanf("%d%d%d",&cup[0],&cup[1],&cup[2]);
fscanf(fin,"%d%d%d",&cup[0],&cup[1],&cup[2]);
for(i=cup[2];i>=0;i--)
{
if(hash[i])path[t++]=i; //path记录可以出现的状态;
else
if(bfs(0,0,cup[2],i))
{
path[t++]=i; //搜索C是否存在i状态;
hash[i]=1;
}
}

qsort(path,t,sizeof(int),cmp);
for(i=0;i<t;i++)
{
if(flag==0)
{
// printf("%d",path[i]);
fprintf(fout,"%d",path[i]);
flag=1;
}
else
fprintf(fout," %d",path[i]);
}
fprintf(fout,"\n");

return 0;
}

貌似是usaco最快的代码,它的效率高在hash可以帮助跳过已经判断过的目标状态避免重复,又因为它是将i一个个枚举的,所以不会有遗漏。

Number triangles

好像没有神马麻烦......,呃...唯一的麻烦就是提交完后弹出个网页刷出满屏的数字,很暴力,我赶紧关了。

Prime palindrome

先构造回文再生成素数比较方便,题目本身不难,不过涉及到2个很有用的知识点。

素数筛(链接中给出的是线性时间的素数筛)和

回文构造

int ppalind()
{
int d1,d2,d3,d4,t=0;
long temp;
for(d1=1;d1<=9;d1+=2) //回文数的个位数;
{
temp=d1; //1位回文
if(temp>b)continue;
if(is_prime(temp)&&temp>=a)p[t++]=temp;
temp=11*d1;
if(temp>b)continue;
if(is_prime(temp)&&temp>=a)p[t++]=temp;
for(d2=0;d2<=9;d2++)
{

temp=d1*101+10*d2; //2位回文
if(temp>b)continue;
if(is_prime(temp)&&temp>=a)p[t++]=temp;
temp=d1*1001+d2*110; //3位回文
if(temp>b)continue;
if(is_prime(temp)&&temp>=a)p[t++]=temp;
for(d3=0;d3<=9;d3++)
{
temp=10001*d1+1010*d2+100*d3;
if(temp>b)continue;;
if(is_prime(temp)&&temp>=a)p[t++]=temp;
temp=100001*d1+10010*d2+1100*d3;
if(temp>b)continue;
if(is_prime(temp)&&temp>=a)p[t++]=temp;
for(d4=0;d4<=9;d4++)
{
temp=1000001*d1+100010*d2+10100*d3+1000*d4;
if(temp>b)continue;
if(is_prime(temp)&&temp>=a)p[t++]=temp;
temp=10000001*d1+1000010*d2+100100*d3+11000*d4;
}
}
}
}
return t;
}

套用网友的原话(哪里看见的忘了):回文是构造出来的,不是判定出来的。

superprime rib

一位位枚举,每一位都跳过偶数和5,这样下来还是很轻松的......

checker challenge

经典的n皇后问题啊...

可以通过旋转来优化,减少枚举量,用位运算最好,但我对此一窍不通,还是只讨论dfs吧。

一条非常非常非常重要(对个人而言吧)的经验:嵌套的循环会给效率带来非常非常非常大的影响,这么说当然是废话,不过通过这题我深刻感受到了。。。。!

版本1:

void dfs(int cur,int row){
int i,j,ok;
col[cur]=row;
if(cur==n-1)
{
total[col[0]]++;
}
for(i=0;i<n;i++)
{
ok=1;
for(j=0;j<cur+1;j++)
{
if(i==col[j]||i+cur+1==j+col[j]||i-cur-1==col[j]-j)
{
ok=0;
break;
}
}
if(ok)dfs(cur+1,i);
}
}


版本2:

void dfs(int x,int y)
{
int i;

v[x]=y;
if(x==n)
{
s[v[1]]++;
return;
}
for(i=x+1;i<=n;i++)
{
f[i][y]+=1;
if(y+i-x<=n) f[i][y+i-x]+=1;
if(y-(i-x)>0) f[i][y-(i-x)]+=1;
}
for(i=1;i<=n;i++)
if(! f[x+1][i])dfs(x+1,i);
for(i=x+1;i<=n;i++)
{
f[i][y]-=1;
if(y+i-x<=n) f[i][y+i-x]-=1;
if(y-(i-x)>0) f[i][y-(i-x)]-=1;
}
}


第一种是我写的,当时为了偷懒,没有开数组判重,直接在第一个for里循环判重了;

第二种开了个f数组判定格子是否被占用,只需在外部循环赋值和清除,看起来for要多一些,但它效率高,

第一种对称优化后的时间是第二种的2倍多,若不优化......天长地久啊 - -!

都是裸艘,东西长对位置就会苗条。。。。。

usaco第一章终于完毕了,计划先学位运算,秒杀n皇后再进入第2章.

本文使用Blog_Backup未注册版本导出,请到soft.pt42.com注册。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: