1. 程式人生 > >hdu1584 蜘蛛牌(經典dfs)

hdu1584 蜘蛛牌(經典dfs)

真是好題啊。。。

剛開始一看下一個狀態是由上一個子問題得來的,想DP上去了,結果找不出狀態方程,一百度是數位DP,還是dfs吧= =。。。

這題的dfs也很奇葩,我對dfs理解還淺啊,剛開始怎麼也想不到怎麼用dfs還不在更新陣列的情況下。於是。。。對我來說還是太難了T T

這題越想越牛逼,本來我還不服,怎麼實現移動的牌按字典序?

我們來個例子,先是最簡單的1,2,3,4順序:

1上,標記1,看到2,遞迴;1被標記,2上,標記2,看到3 ,遞迴;1、2被標記,3上, 標記3, 看到4,遞迴;返回最優解。

再來2,1,3,4順序:

1上,標記,看到2,遞迴;。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。此過程類似上面

第二次就不一樣了!

2上,標記,看到3,遞迴;1未被標記,1上,標記1,看到3,遞迴。。。。。。。。。。。。。。。同類似

使用的牌按順序字典序固然好理解,當打亂的時候同樣是按照字典序,這就是對遞迴領悟境界的更高層次啊!

ps:語文不好還加點頭暈哭(貌似要感冒)表達不好,不過我覺得這就是自己憑空想!誰都幫不了你,在腦海中模擬樹的構造,這才是dfs的精髓!(個人理解勿噴)。。

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

const int N = 100;
const int INF = 1000000;

char a[N], mark[N];
int ans;

void dfs(int num, int sum)
{
    if(sum >= ans) return;//剪枝
    if(num == 9)//剛開始我以為是大於9的解都被剪掉了,現在想這本來就是個9層樹。。。
    {
        ans = sum;
        return;
    }
    for(int i = 1; i < 10; i ++)//最後一張牌除空位(已標記)外沒有任何移動的地方,本語句代表現在準備移動(操作)哪張牌
    {
        if(!mark[i])
        {
            mark[i] = 1;
            for(int j = i + 1; j <= 10; j ++)//移動到哪個位置。注意這裡不一定是移動到5號牌(打個比方)後的位置,也有可能是5號牌前的位置。因為每次dfs相當於生出一層樹枝,而生出的樹枝又是從1號牌開始操作,也就是說只要2、3、4號牌沒被標記,還是有可能移動到這裡,而移到這裡的是1號牌而不是5號牌
            {
                if(!mark[j])
                {
                    dfs(num + 1, sum + abs(a[j] - a[i]));
                    break;//這裡的直觀意義就是下一層樹枝返回,此牌所找的位置安放不對或尋求其他最優解,而for是為了遍歷所有解,是一個意思,所以退出
                }
            }
            mark[i] = 0;//回溯
        }
    }
}

int main()
{
   // freopen("in.txt", "r", stdin);
    int T, x;
    scanf("%d", &T);
    while(T --)
    {
        for(int i = 1; i <= 10; i ++)
        {
            scanf("%d", &x);
            a[x] = i;//儲存每張牌在哪個位置,比如6號牌在位置2
        }
        memset(mark, 0, sizeof(mark));
        ans = INF;
        dfs(0, 0);
        printf("%d\n", ans);
    }
    return 0;
}