HDU1584(dfs回溯 或 区间dp)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1584


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

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

#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
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

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

下面是代码:

#include
#include
#include
#include
#include
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




你可能感兴趣的:(动态规划,搜索)