蜘蛛牌

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的位置上,因为只能把小牌移动到大牌上!

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

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

DP做法:

我们在合牌的过程中总会把牌分成了最后的两堆,然后将这两堆合并后合并完成!

那么有多少种这样的可能呢?,显然:1-->k,k+1-->10(1<=k<10),不妨用一个二维数组记录这个操作,

dp[i][j]表示将牌面排成i->i+1->i+2......j需要的最少移动距离,那么显然我们需要的答案是dp[1][10];

由此得出状态转移方程: dp[i][j]=MIN(dp[i][k]+dp[k+1][j]+abs(a[k]-a[j]) | (i<=k<j) )

假设i,j表示面值,

显然想求得跨度为n=abs(i-j)的dp[i][j]的话需要跨度为1-->n-1的dp值才可继续递推下去,因此我们应该从跨度由低到高递推即可:

#include<bits/stdc++.h>
using namespace std;
int dp[11][11],a[11];
int main()
{
int t,i,j,k,e;
cin>>t;
while(t--){memset(dp,0,sizeof(dp));
for(i=1;i<=10;++i) cin>>e,a[e]=i;
for(k=1;k<10;++k)                             //枚举跨度
for(i=1;i+k<=10;++i){e=i+k;int M=999999999;
for(int temp=i;temp<e;++temp)
M=min(M,dp[i][temp]+dp[temp+1][e]+abs(a[temp]-a[e]));                //找出最优的分割点
dp[i][e]=M;
}
cout<<dp[1][10]<<endl;
}
return 0;
}

hdu1584的更多相关文章

  1. HDU-1584 蜘蛛牌(dfs)

    可以多看看. 蜘蛛牌 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  2. HDU1584:蜘蛛牌(DFS)

    Problem Description 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么 ...

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

    A strange lift Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) T ...

  4. HDU1584(蜘蛛牌)

    蜘蛛牌 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  5. ACM学习历程—HDU1584 蜘蛛牌(动态规划 && 状态压缩 || 区间DP)

    Description 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也跟着一起 ...

  6. hdu1584(状态压缩DP)

    蜘蛛牌 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

随机推荐

  1. javascript闭包(Module模式)的用途和高级使用方式

    javascript闭包(Module模式)的用途和高级使用方式 javascript闭包的用途:1. 匿名自执行函数:或者可以理解为,避免污染全局变量2. 缓存:源于闭包的核心特性便是保存状态,应用 ...

  2. CentOS随笔 - 4.CentOS7安装MySql 5.5.60(下载 tar 方式安装)

    前言 转帖请注明出处: http://www.cnblogs.com/Troy-Lv5/ 由于公司也有php+mysql的项目, 所以今天也把Mysql装了一遍. 为了与以前的程序和数据库兼容, 这次 ...

  3. 如何向GLSL中传入多个纹理

    http://blog.csdn.net/huawenguang/article/details/41245871 如何向GLSL中传入多个纹理 这几天在研究如何实现用GLSL对多个纹理进行融合处理, ...

  4. Python Web笔记之高性能网络编程

    请参考博客: https://blog.csdn.net/russell_tao/article/details/9111769

  5. Java 第二次实验20145104 张家明

    实验二 Java面向对象程序设计 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计模式 实验步骤 (一)单元测试 ...

  6. 20145127《java程序设计》第五周学习总结

    教材学习内容总结 第八章 异常处理 1.try catch java中所有错误会被打包成对象,可以进行尝试捕捉代表错误的对象进行处理. Import java.until.Scanner; Publi ...

  7. 「不定期更新」MacOS 编辑器使用小技巧

    Visual Studio Code Ctrl + CMD + 上下箭头:上下移动当前的代码块: Shift + Alt + 上下箭头:快速复制当前的代码块: 我最新欢的插件列表: Prettier: ...

  8. Codeforces Round #527 (Div. 3)

    一场div3... 由于不计rating,所以打的比较浪,zhy直接开了个小号来掉分,于是他AK做出来了许多神仙题,但是在每一个程序里都是这么写的: 但是..sbzhy每题交了两次,第一遍都是对的,结 ...

  9. centos6下通用二进制格式安装MySQL过程

    1.首先确保主机的MySQL没有运行 #ss -tnl  //查看有没有80端口 或者 #service mysqld stop 2.添加mysql用户和组 #id mysql  //首先查看mysq ...

  10. BZOJ 1006: [HNOI2008]神奇的国度(弦图染色)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1006 题意: 思路: 这个就是弦图染色问题,弦图啥的反正我也不懂,具体看论文https://wenk ...