HDU1584(dfs回溯 或 区间dp)

来源:互联网 发布:淘宝金币怎么提现 编辑:程序博客网 时间:2024/05/16 19:01
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1584


题解: 我的思路:这道题吧!最开始我是想找到从头到尾每个牌都尝试一下第一个移动,然后因为每次都是移动自己标记的那一个位置的牌,所以没有做到枚举所有情况,感觉肯定有人和我想法一样,先把错误代码贴上,大家引以为鉴

错误代码(注意!是错误代码):

#include<cstdio>#include<string>#include<cstring>#include<iostream>#include<cmath>#include<algorithm>using namespace std;int ans;struct Node{    int up, down, sum;} node[11];void dfs(int x, int step){    if(node[x].sum == 10) {ans = min(ans, step);return ;}    for(int i = 1; i <= 10; i++)    {        if(node[i].sum == 0) continue;        else if(node[i].down == node[x].up+1)        {            node[i].down -= node[x].sum;            node[i].sum += node[x].sum;            int temp = node[x].sum;            node[x].sum = 0;            dfs(i, step+abs(x-i));            node[x].sum = temp;            node[i].sum -= node[x].sum;            node[i].down += node[x].sum;        }        else if(node[x].down == node[i].up+1)        {            node[x].down -= node[i].sum;            node[x].sum += node[i].sum;            int temp = node[i].sum;            node[i].sum = 0;            dfs(x, step+abs(i-x));            node[i].sum = temp;            node[x].sum -= node[i].sum;            node[x].down += node[i].sum;        }    }}int main(){    int N;    scanf("%d", &N);    while(N--)    {        for(int i = 1; i <= 10; i++)        {            scanf("%d", &node[i].up);            node[i].down = node[i].up;            node[i].sum = 1;        }         ans = 0x3f3f3f3f;        for(int i = 1; i <= 10; i++)        {            dfs(i, 0);        }        printf("%d\n", ans);    }}
然后看了别人的题解以后发现别人的代码更精简,重点是他是对的。安静思路是因为只有十张牌,所以最多只可能移动九次。然后开一个a[i] = j数组, i表示这个牌的大小,j表示这个牌的位置,然后就是dfs了,dfs结束的标志是当深度达到9的时候,然后每次找一个位置把这个位置的牌移动一下,然后再找一个位置是这个牌要移动到的位置,vis数组表示这个牌是否移动过,移动过的话,就找比这个牌的大的,因为移动过的牌只可能移动到比他大的牌上,这一步感觉有点像并查集里的找老大。然后下面附上代码:

#include<cstdio>#include<cstring>#include<string>#include<cmath>#include<algorithm>#include<iostream>using namespace std;int T, vis[12], a[12], ans;void dfs(int deep, int sum){    if(sum > ans) return;    if(deep == 9) {ans = min(ans, sum); return;}    for(int i = 1; i < 10; i++)    {        if(!vis[i])        {            int to;            for(int j = i+1; j <= 10; j++)                if(!vis[j])            {                to = j;                break;            }            vis[i] = 1;            dfs(deep+1, sum+abs(a[i]-a[to]));            vis[i] = 0;        }    }}int main(){    scanf("%d", &T);    while(T--)    {        ans = 0x3f3f3f3f;        memset(vis, 0, sizeof(vis));        for(int i = 1; i <= 10; i++)        {            int temp;            scanf("%d", &temp);            a[temp] = i;        }        dfs(0, 0);        printf("%d\n", ans);    }}

区间dp的方法:做过几道区间dp的题,但是现在看区间dp还是不太理解是为什么?然后作者就去潜心修行了一下,最终参透了区间dp的真谛(九牛一毛),所以我现在就兴冲冲的来圆我夸下的海口,其实在我看来,这个区间dp是不难的。一般他的状态转移方程是这个样子的:
       for(int len = 2; len<=10; len++)       {           for(int l=1, r=l+len-1; r<=10; l++, r++)           {               dp[l][r] = inf;               for(int k = l; k<r; k++)                dp[l][r] = min(dp[l][r], dp[l][k]+dp[k+1][r]+abs(a[k]-a[r]));           }       }

,我苦思冥想,为什么要苦思冥想呢,因为我比较菜吧!我个人理解就是先把相邻大小的两个牌合并,然后全部的情况遍历完了,开始合并三个,然后逐渐增加到十个,结果就出来了。

下面是代码:

#include<cstdio>#include<cstring>#include<iostream>#include<cmath>#include<algorithm>using namespace std;const int inf = 0x3f3f3f3f;int dp[20][20];int main(){   int T;   scanf("%d", &T);   while(T--)   {       int in, a[20];       for(int i = 1; i<=10; i++) scanf("%d", &in), a[in] = i;       for(int i = 1;i<=10; i++)dp[i][1] = 0;       for(int len = 2; len<=10; len++)       {           for(int l=1, r=l+len-1; r<=10; l++, r++)           {               dp[l][r] = inf;               for(int k = l; k<r; k++)                dp[l][r] = min(dp[l][r], dp[l][k]+dp[k+1][r]+abs(a[k]-a[r]));           }       }       cout<<dp[1][10]<<endl;   }   return 0;}




1 0
原创粉丝点击