学习了高级一些的搜索算法,当然考试是免不了的,于是就有了噩梦般的4个小时……
题录
UVA11549
UVA11212
UVA11882
UVA11549
题意
给你一个n位数作为限定,k作为原数。每次操作将k进行平方,然后取前面n位作为新的k值。
询问做若干次操作,能得到的最大的k值。
思路
对于本题,什么时候停止操作是关键所在。由于n<=9,显然直接用bool判重会导致MLE,用map就会TLE。不过,写hash我倒是没有尝试过,本人太水,hash写不好……那么可以用一个神奇的算法——龟兔赛跑算法,也叫floyd判圈算法。没有听说过的,可以了解了解。
它的原理还是很好理解的。使一个快指针(fast),和一个慢指针(slow)从某一位置出发。每次fast去到下一个结点的下一个结点,slow去到下一个结点,如若存在一个环,那么如此循环,直到fast=slow,此时fast必定已经遍历过整个环,既可以保证遍历整个环,又可以得到终止条件。
代码
#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
typedef unsigned long long ll;
template <typename Tp> void read(Tp &x)
{
x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
ll z,n,k,fast,slow,maxx,limit[10]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
ll calc(ll n,ll num)
{
num*=num;
while(num>=limit[n])
num/=10;
return num;
}
int main()
{
freopen("calc.in","r",stdin);
freopen("calc.out","w",stdout);
read(z);
while(z--)
{
read(n);
read(k);
maxx=k;
fast=slow=k;
do{
fast=calc(n,fast);
maxx=max(fast,maxx);
fast=calc(n,fast);
maxx=max(fast,maxx);
slow=calc(n,slow);
}while(fast!=slow);
printf("%llu\n",maxx);
}
return 0;
}
UVA11212
题意
给你一段数列,每次操作可以将某段连续的数列插入到任意位置,询问最少需要多少次操作才能使这段数列变成有序的。
思路
令变换的序列为[i,j],插入到k的后方位置。
由于n<=9,那么5步之内必然能出解,另外每次操作可以改变3个数的后继,即i-1,j,k,所以我们就有了两个很重要的剪枝策略。然后剩下的就是纯模拟了。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int n,depth,a[16];
bool found,flag;
template <typename Tp> void read(Tp &x)
{
x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
bool check()
{
for(int j=1;j<=n;j++)
if(a[j]!=j)
return false;
return true;
}
int h()
{
int ans=0;
for(int j=0;j<=n;j++)
if(a[j]+1!=a[j+1])
ans++;
return ans;
}
void dfs(int d)
{
if(check())//每次都要检测
found=true;
if(found||(d-1)*3+h()>depth*3)
return ;
int i,j,k,p,tmp[16];
for(i=1;i<=n;i++)
{
for(j=i;j<=n;j++)
{
for(k=1;k<=i-1;k++)
{
memcpy(tmp,a,sizeof(tmp));
for(p=k; p<=i-1; p++)
a[p+j-i+1]=tmp[p];
for(p=i; p<=j; p++)
a[p-i+k]=tmp[p];
dfs(d+1);
if(found)
return ;
memcpy(a,tmp,sizeof(tmp));
}
for(k=j+1; k<=n; k++)
{
memcpy(tmp,a,sizeof(tmp));
for(p=j+1; p<=k; p++)
a[p-j+i-1]=tmp[p];
for(p=i; p<=j; p++)
a[p+k-j]=tmp[p];
dfs(d+1);
if(found)
return ;
memcpy(a,tmp,sizeof(tmp));
}
}
}
}
int main()
{
freopen("hanoi.in","r",stdin);
freopen("hanoi.out","w",stdout);
while(1)
{
read(n);
if(n==0)
break;
flag=true;
for(int i=1;i<=n;i++)
{
read(a[i]);
if(a[i]!=i)
flag=false;
}
a[0]=0,a[n+1]=n+1;
if(flag)
{
printf("0\n");
continue ;
}
found=depth=0;
while(!found)
{
depth++;
dfs(1);
if(depth>=4)
break;
}
if(found)
printf("%d\n",depth);
else
printf("5\n");
}
return 0;
}
UVA11882
题意
给你一个地图,地图包括1~9和#字符,#是不能进入的,你可以从任意一个可走的地方出现,并不断移动到周围四个格子,但不能走重复的路径,每到一个可走的格子,价值的变换是规则是将新格子中的数接到当前价值的后面一位,即:now=now*10+value.
思路
由于n*m<=30,如果把价值当做数来储存,还是有点麻烦的,毕竟unsigned long long也还未长过20位,而且又因为变换规则是往后接,这符合string类型的加法规则,于是我们便想到用string类型来作为存储变量。
其次,当有极限数据时,比如地图中全是数字,那么就很容易被卡时间。那么,就也需要一个估价函数来进行剪枝,即广搜一遍可以到达的所有点数,如果可以到达的所有点数都无法使它比已经搜到的最优答案长度更长,那么就可以直接剪枝。
值得注意的是,不要规规矩矩地去算当前点能够合法到达的路径最大长度!因为我们只需要一个大致的估价函数,准确的去算,反而使这个估价函数加大时间复杂度,何况一般情况下,所有的点与最大长度也差不了太多。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=30;
struct date
{
int x,y;
};
int n,m,v[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
bool b[maxn][maxn],ht[maxn][maxn];;
char map[maxn][maxn];
string ans,tmp;
date q[maxn*maxn];
bool cmp(string a,string b)
{
int lena=a.length(),lenb=b.length();
return (lena>lenb)||(lena==lenb&&a>b);
}
int h(int x,int y)
{
int head=0,tail=0,cnt=0;
q[tail].x=x;
q[tail++].y=y;
memcpy(ht,b,sizeof(ht));
while(head!=tail)
{
for(int i=0;i<4;++i)
{
int xx=q[head].x+v[i][0],yy=q[head].y+v[i][1];
if(xx>=0&&xx<n&&yy>=0&&yy<m&&map[xx][yy]!='#'&&!ht[xx][yy])
{
q[tail].x=xx;
q[tail++].y=yy;
++cnt;
ht[xx][yy]=1;
}
}
head++;
}
return cnt;
}
void dfs(int x,int y,string s,int deep)
{
int l=h(x,y);
if(l+deep<ans.size())
return;
if(l+deep==ans.size()&&(s+"z")<ans)
return;
if(cmp(s,ans))
ans=s;
for(int i=0;i<4;++i)
{
int xx=x+v[i][0],yy=y+v[i][1];
if(xx>=0&&xx<n&&yy>=0&&yy<m&&map[xx][yy]!='#'&&!b[xx][yy])
{
b[xx][yy]=true;
dfs(xx,yy,s+map[xx][yy],deep+1);
b[xx][yy]=false;
}
}
}
int main()
{
freopen("mine.in","r",stdin);
freopen("mine.out","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF,n||m)
{
ans="";
for(int i=0;i<n;++i)
scanf("%s",map[i]);
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
if(map[i][j]!='#')
{
memset(b,0,sizeof(b));
b[i][j]=1;
tmp="";
tmp+=map[i][j];
dfs(i,j,tmp,1);
}
cout<<ans<<"\n";
}
return 0;
}
由此可见,当一个题目要卡时间时,A*的思想是很重要的,写好一个估价函数,可以帮助减去很多不必要的计算。
另外还接触到了龟兔赛跑算法,很不错的算法。收获颇丰。