http://acm.hdu.edu.cn/showproblem.php?pid=1584
蜘蛛牌
Description
蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也跟着一起移动,游戏的目的是将所有的牌按同一花色从小到大排好,为了简单起见,我们的游戏只有同一花色的10张牌,从A到10,且随机的在一行上展开,编号从1到10,把第i号上的牌移到第j号牌上,移动距离为abs(i-j),现在你要做的是求出完成游戏的最小移动距离。
Input
第一个输入数据是T,表示数据的组数。
每组数据有一行,10个输入数据,数据的范围是[1,10],分别表示A到10,我们保证每组数据都是合法的。
Output
对应每组数据输出最小移动距离。
Sample Input
1 1 2 3 4 5 6 7 8 9 10
Sample Output
9
程序:
#include<stdio.h>
#include<math.h>
int d[11][11];
int f(int a,int b)
{
int i,temp,min=99999;
if(a==b)
return 0;
else if(b-a==1)
return d[a][b];
for(i=a+1;i<=b;i++){
temp=f(a+1,i)+f(i,b)+d[a][i];
if(temp<min)
min=temp;
}
return min;
}
void main()
{
int x[11],t,i,n,j,m,k;
while(scanf("%d",&m)!=EOF){
for(k=1;k<=m;k++){
for(i=1;i<=10;i++){
scanf("%d",&t);
x[t]=i;
}
for(i=1;i<=10;i++)
for(j=1;j<=10;j++)
d[i][j]=abs(x[i]-x[j]);
printf ("%d/n",f(1,10));
}
}
}
实例:
10 9 8 7 6 5 4 3 2 1
for(i=1;i<=10;i++){
scanf("%d",&t);
x[t]=i;
}
i : 1 2 3 4 5 6 7 8 9 10
x[i]:10 9 8 7 6 5 4 3 2 1
x[i] 表示 牌 i 所在的位置
然后大张表 d[ i ][ j ] 表示 牌 i 与 牌 j 之间的距离
for(i=1;i<=10;i++)
for(j=1;j<=10;j++)
d[i][j]=abs(x[i]-x[j]);
d[ i ][ j ] 为:
0 1 2 3 4 5 6 7 8 9
1 0 1 2 3 4 5 6 7 8
2 1 0 1 2 3 4 5 6 7
3 2 1 0 1 2 3 4 5 6
4 3 2 1 0 1 2 3 4 5
5 4 3 2 1 0 1 2 3 4
6 5 4 3 2 1 0 1 2 3
7 6 5 4 3 2 1 0 1 2
8 7 6 5 4 3 2 1 0 1
9 8 7 6 5 4 3 2 1 0
最后 printf ("%d/n",f(1,10));
接下来在看调用函数 f ( a , b )之前先看纸牌移动有何特殊之处
如果是有下面这种移动方法:
经过一系列的移动,不妨设为 k 次,而第 k+1 次轮到牌 1 移动;
如果情况如下:
* * * * * * * * * * *
10 9 8 7 6 5
4
3
2
1
可见牌 1 所在那堆最上面的牌是牌 5 ,那么牌 2 到牌 5 之间的牌肯定已经全部排好了;
那就是说对于牌 1 第一次移动后,它的最上面是牌 5 的话,那么只要使牌 2 到牌 5 四张牌移动的距离最小且牌 5 到牌 10 六张牌移动的距离最小。
那么移动的最小总距离 D=Min f ( 2 , i ) +f ( i , 10 ) +d[ 1 ][ 5 ];
而 1 第一次移动后它最上面的牌可以是 f( i = 2; i<=10; i++ )
那么只要取这 9 种情况中 D 最小的那个值即可;
for(i=2;i<=10;i++){
temp=f(2,i)+f(i,10)+d[1][i];
if(temp<min)
min=temp;
}
return min;
这样 f ( a , b )便为
for(i=a;i<=b;i++){
temp=f(a+1,i)+f(i,b)+d[a][i];
if(temp<min)
min=temp;
}
return min;
其中如果 a==b 时则 return 0 ;
a-b==1 时可以 return d [ a ][ b ] ;(也可省略), 即:
int f(int a,int b)
{
int i,temp,min=99999;
if(a==b)
return 0;
else if(b-a==1)
return d[a][b];
for(i=a+1;i<=b;i++){
temp=f(a+1,i)+f(i,b)+d[a][i];
if(temp<min)
min=temp;
}
return min;
}