LightOJ 1057 Collecting Gold(状压DP)

题目

nm,(n,m)<(20,20) 的格子图上有一个人,和不超过15个的金矿;求这个人从当前位置出发获取所有金矿然后再回到这个位置需要走的最少路程?每次只能往邻近的四个方向走;

思路

因为没有障碍物,所以算两个格子间的距离很方便;
开始用的穷举所有的获取金矿的先后顺序,然后TLE了;
最后就状压过了,dp[sta][i]表示达到状态sta在第i个金矿上的最小路程;

char mp[22][22];
int dp[(1 << 16) + 1][16];
int dis(ii a, ii b) {
    return max(abs(a.first - b.first) , abs(a.second - b.second));
}
int main(int argc, const char * argv[])
{
    int kase;cin >> kase;
    while(kase--) {
        int n, m;scanf("%d%d", &n, &m);
        Rep(i, 1, n) scanf("%s", mp[i] + 1);
        ii st;
        vector<ii> v;
        Rep(i, 1, n) {
            Rep(j, 1, m) {
                if (mp[i][j] == 'x') st = ii(i, j);
                if (mp[i][j] == 'g') v.push_back(ii(i, j));
            }
        }
        memset(dp, inf, sizeof dp);
        int Size = (int)v.size();
        for (int i = 0;i < Size;++i)
            dp[1 << i][i] = dis(st, v[i]);
        for (int sta = 1;sta < 1 << Size;++sta) {
            for (int i = 0;i < Size;++i) {
                if ((sta & (1 << i)) && dp[sta][i] != inf) {
                    for (int j = 0;j < Size;++j) {
                        if ((sta & (1 << j)) == 0) {
                            //i -> j
                            dp[sta | (1 << j)][j] = min(dp[sta | (1 << j)][j], dp[sta][i] + dis(v[i], v[j]));
                        }
                    }
                }
            }
        }
        int ans = inf;
        for (int i = 0;i < Size;++i)
            ans = min(ans, dp[(1 << Size) - 1][i] + dis(v[i], st));
        if (Size == 0) ans = 0;
        printf("Case %d: %d\n", ++nCase, ans);
    }

    // showtime;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/KIJamesQi/article/details/55099976