hdu1584

蜘蛛牌

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3761    Accepted Submission(s):
1606

Problem 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

Author

xhd

Source

很久之前...开学初吧困扰自己的一道破题,当时竟然连爆搜都没想到。

这个牌任意移动,所以每次的出发点并不固定,当时一直按照固定的方法来做自然WA了= =,今天写刚开始是个无剪纸爆搜2000+ms。。。。

后来发现剪枝:当总步数大于当前最小步数时,直接return,总算卡进1000ms内了,300+,看榜单都是0ms。。。原来还能用区间dp求解,而且我发现自己把这个搜索想的复杂了。

仔细想想,假设从一个位置移动到另一个位置算一次移动的话,无论怎么操作,最终(不做无意义的移动时)都要移动九次即可,只是总步数不同而已,一开始并没有想到这个!

原先搜索代码:

#include<bits/stdc++.h>
using namespace std;
struct Pock                                  //结构体保存当前位置最小和最大牌面值
{
int up,down;
}a[15];
int s;
int debug=10;
bool pd()
{
int book=0,i;
for(i=1;i<=debug;++i)
if(a[i].up==0&&a[i].down==0) ++book;
return (book==debug-1?1:0);
}
void dfs(int sumn)
{
if(sumn>=s) return;                              //剪枝1,步数大于最小时直接return
if(pd()){
if(sumn<s) s=sumn;
return;
}

for(int i=1;i<=debug;++i){
if(a[i].up!=0&&a[i].down!=0){
for(int j=1;j<=debug;++j){
if(i==j) continue;
if(a[i].up+1==a[j].down){
int tup=a[i].up;
int tdown=a[i].down;
int jdown=a[j].down;
a[i].up=a[i].down=0;
a[j].down=tdown;

dfs(sumn+abs(i-j));

a[i].up=tup;
a[i].down=tdown;
a[j].down=jdown;

break;                                         //剪枝2,比i位置up值大一的位置只会出现一个,所以搜索完直接break;
}
}
}
}
}
int main()
{
int n,t,i,j,c;
scanf("%d",&t);
while(t--){s=1e9;
for(i=1;i<=debug;++i){
scanf("%d",&c);
a[i].up=c;
a[i].down=c;
}
dfs(0);
printf("%d\n",s);
}
return 0;
}

优化后的剪枝:

#include<bits/stdc++.h>
using namespace std;
int a[15],s;
bool vis[15];
void dfs(int cur,int sumn)
{
if(sumn>=s) return;
if(cur==10){
s=sumn;
return;
}
for(int i=1;i<=10;++i){
if(!vis[i]){
vis[i]=1;
for(int j=i+1;j<=10;++j){
if(!vis[j]){
dfs(cur+1,sumn+abs(a[i]-a[j]));
break;
}
}
vis[i]=0;
}
}
}
int main()
{
int i,j,t;
cin>>t;
while(t--){s=1e10,memset(vis,0,sizeof(vis));
for(i=1;i<=10;++i) {cin>>j;a[j]=i;}
dfs(1,0);
cout<<s<<endl;
}
return 0;
}

优化原理:

a[i]即面值为i的牌所在位置,之所以这样写是为了方便搜索,

一:面值为m的牌的位置只可能出现在面值>=m的位置上,因为只能把小牌移动到大牌上!

二:移动次数超过九次说明已经移动完毕!

这个写法简直不能再帅了!!!

时间: 2024-11-05 01:46:51

hdu1584的相关文章

ACM学习历程—HDU1584 蜘蛛牌(动态规划 &amp;&amp; 状态压缩)

Description 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也跟着一起移动,游戏的目的是将所有的牌按同一花色从小到大排好,为了简单起见,我们的游戏只有同一花色的10张牌,从A到10,且随机的在一行上展开,编号从1到10,把第i号上的牌移到第j号牌上,移动距离为abs(i-j),现在你要做的是求出完成游戏的最小移动距离. Input 第一个输入数据是T,表示数据的组数.

hdu1584 深搜水题

牌移动的步数为牌所在位置差的绝对值 这道题用到深搜 每次出差跑牌面为1-9的牌 如果没移动过则尝试移动 如果能移动 就深搜下去 注意回溯 #include<stdio.h> #include<string.h> #include<iostream> using namespace std; int mark[15],num[15],Min; int abs(int a) { return a>0?a:-a; } int dfs(int sum,int coun)

HDU1584 蜘蛛牌 DFS 简单题

题意: 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也跟着一起移动,游戏的目的是将所有的牌按同一花色从小到大排好,为了简单起见,我们的游戏只有同一花色的10张牌,从A到10,且随机的在一行上展开,编号从1到10,把第i号上的牌移到第j号牌上,移动距离为abs(i-j),现在你要做的是求出完成游戏的最小移动距离. 输入: 第一个输入数据是T,表示数据的组数.每组数据有一行,10

hdu1584 A strange lift (电梯最短路径问题)

A strange lift Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 15570    Accepted Submission(s): 5832 Problem Description There is a strange lift.The lift can stop can at every floor as you want

HDU1584(蜘蛛牌)

蜘蛛牌 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2880    Accepted Submission(s): 1230 Problem Description 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也

hdu1584(状态压缩DP)

蜘蛛牌 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2068    Accepted Submission(s): 844 Problem Description 蜘 蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也