蜘蛛牌

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. 用户可在Messenger内读书并与角色互动

    畅销小说作家 James Patterson 与 Facebook 合作,为新书<The Chef>在 Messenger 推出互动体验版. 10 月 30 日下午消息,据<香港经济 ...

  2. 深入JAVA注解之方法注解

    以获取数据库连接为例,建立maven项目 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=" ...

  3. c/c++的typedef/using类型别名

    久而久之,发现c/c++的typedef给类型自定义别名的语法糖就保证设计的一致性而言,确实是个相当不错的特性,跟oracle pl/sql的rowtype或type一样,可惜java.mysql均不 ...

  4. 20144303石宇森《网络对抗》Web安全基础实践

    20144303石宇森<网络对抗>Web安全基础实践 实验后问题回答 SQL注入攻击原理,如何防御: SQL攻击时通过在输入框中输入语句,构造出SQL命令,把这段命令注入到表单中,让后台的 ...

  5. 解决Vue循环中子组件不实时更新的问题

    问题描述 使用Element-UI中的table组件时会遇到一个常见的问题.当在el-table中调用子组件的时候会出现数据更新后,子组件没有重新渲染的问题. eg:资源列表中的健康度组件. 代码如下 ...

  6. 高通RFC适配RFFE-添加MIPI设备【转】

    本文转载自:https://blog.csdn.net/u011212816/article/details/80828625 RF driver主要设计到的器件 1.Transceiver 2.RF ...

  7. Linux slab分配器【转】

    本文转载自:https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/ 良好的操作系统性能部分依赖于操作系统有效管理资源的能 ...

  8. 使用CLR Profiler查看C#运行程序的内存占用情况

    http://blog.csdn.net/wy3552128/article/details/8158938 https://msdn.microsoft.com/en-us/library/ff65 ...

  9. 【附1】hystrix详述(1)

    一.hystrix的作用 控制被依赖服务的延时和失败 防止在复杂系统中的级联失败 可以进行快速失败(不需要等待)和快速恢复(当依赖服务失效后又恢复正常,其对应的线程池会被清理干净,即剩下的都是未使用的 ...

  10. UVa 10723 电子人的基因(LCS)

    https://vjudge.net/problem/UVA-10723 题意: 输入两个A~Z组成的字符串,找一个最短的串,使得输入的两个串均是它的子序列,另外还需要统计长度最短的串的个数. 思路: ...