HDu 3339 In Action 【最短路 + 背包】好题!!!

传送门
// 题意: 每个点有一个电量值, 任意两点之间有一个距离, 每次从0出发到其他点的消耗的油量为到该点的距离, 到了该点后就可以摧毁该点, 注意是每到一个点都要重新从0号点出发, 问至少摧毁总电量的一般至少花费多少的油量.

// 思路: 注意是每新到一个点都是重新从0号点出发的, 所以在一条路上的点不存在依赖关系, 所以这个问题就比较明显了, 首先最少, 肯定是到这个点的最短路, 可以先预处理出来, 其次是总电量的一半,很明显的我们有到一个点它的花费是最短路径最为它的体积, 价值是它的电量, 那不就是01背包吗. 所以我们处理出来后直接做01背包, 算出每一个容量最多得到的价值, 然后从小到大扫一遍输出第一个价值超过总电量一般的那个体积就行啦.

注: 那么总容量最大肯定是所有点的最短距离加起来, 但是这个的复杂度可能会爆, 所以大致猜个1e4左右就行啦. 还有就是这道题卡dij, 应该是某两个点之间的重边很多, 导致它一直pop, 复杂度就爆炸了, 那么由于点只有100个, 我们直接跑floyd就OK啦~.

// 这道题可以用01背包是因为到每一个点没有依赖关系, 从而可以将它的最短路径作为体积. 当然最主要的原因是每一个点是选与不选, 这就是01背包最主要的经典.

AC Code

const int maxn = 1e2 + 5;
int cas = 1 ;
int g[maxn][maxn];
int a[maxn], dp[maxn*maxn];
void solve()
{
    int n,m;
    scanf("%d%d", &n, &m);
    for (int i = 0 ; i <= n ; i ++) {
        for (int j = 0 ; j <= n ; j ++) {
            if (i == j) g[i][j] = 0;
            else g[i][j] = inf;
        }
    }
    for (int i = 1 ; i <= m ; i ++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        if (w < g[u][v]) {
            g[u][v] = g[v][u] = w;
        }
    }
    for (int k = 0 ; k <= n ; k ++)
        for (int i = 0 ; i <= n ; i ++)
            for (int j = 0 ; j <= n ; j ++)
                g[i][j] = min(g[i][j] , g[i][k] + g[k][j]);
    int sum = 0, mx = 0;
    for (int i = 1 ; i <= n ; i ++) {
        scanf("%d", &a[i]);
        sum += a[i];
        if (g[0][i] != inf)
            mx += g[0][i];
    }
    sum /= 2; Fill(dp, 0);
    for (int i = 1 ; i <= n ; i ++) {
        for (int j = mx ; j >= g[0][i] ; j--) {
            dp[j] = max(dp[j], dp[j-g[0][i]] + a[i]);
        }
    }
    int ans = -1;
    for (int i = 1 ; i <= mx ; i ++) {
        if (dp[i] > sum) {
            ans = i;
            break;
        }
    }
    if (ans == -1) cout << "impossible" << endl;
    else cout << ans << endl;
}

你可能感兴趣的:(最短路相关)