bzoj2595 [Wc2008]游览计划

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2595

【题解】

斯坦纳树模板题。学了一发斯坦纳树。

对于一般的斯坦纳树,是 给出一些点和一些关键点和边,要求选择权值和最小的连通块使得关键点连通。

那么一般我们用f(x,status)表示在x,状态为status的最小权值和。

本题我们采用f(i,j,status)表示在(i,j),状态为status的最小权值和。

一开始权值就是题目给的,如果是景点那么在对应的标号的status赋值即可。

首先可以分成两个部分来转移:f(i,j,status) = f(i, j, subset) + f(i, j, status - subset) - mp[i][j]

(也就是枚举子集,因为(i,j)被多算了一次,所以要减掉)

另外一面,f(i, j, status) = min(f(i', j', s') + mp[i][j])

(从周围四个格子转移过来,增加一个格子,如果是景点需要特别处理下s')

这个式子长得非常像最短路,所以我们可以用spfa来解决。

然后就处理完了。接着说输出答案。

我们记录从哪里转移得到即可。递归下去处理答案并输出。

好麻烦啊qwq第一次题解写这么长呀。

不过斯坦纳树应用好像不大多,了解就行。

【update】原来的板子。。这题能过但是其他题目好像跑的贼慢,更新了新板子

# include <queue>
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 10 + 10, STATUS = 1027, N = 500010;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, m, k = 0;
int mp[M][M], st[M][M]; 
int f[M][M][STATUS]; 
const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1}; 

struct pa {
    int i, j, s;
    pa() {}
    pa(int i, int j, int s) : i(i), j(j), s(s) {}
    inline int set() {
        return i+11*j+11*11*s; 
    }
    friend bool operator == (pa a, pa b) {
        return a.i == b.i && a.j == b.j && a.s == b.s;
    }
};

pa from[M][M][STATUS]; 
queue< pair<int,int> > q;
bool vis[M][M];
bool ans[M][M];

inline void spfa(int status) {
    for (int i=1; i<=n; ++i)
        for (int j=1; j<=m; ++j)
            vis[i][j] = 1, q.push(make_pair(i, j));
    while(!q.empty()) {
        pair<int,int> top=q.front(); q.pop(); vis[top.first][top.second] = 0; 
        for (int i=0; i<4; ++i) {
            int xx=top.first+dx[i], yy=top.second+dy[i];
            if(xx<1||xx>n||yy<1||yy>m) continue;
            if(f[xx][yy][status] > f[top.first][top.second][status] + mp[xx][yy]) {
                f[xx][yy][status] = f[top.first][top.second][status] + mp[xx][yy];
                from[xx][yy][status] = pa(top.first, top.second, status);
                if(!vis[xx][yy]) {
                    vis[xx][yy] = 1;
                    q.push(make_pair(xx, yy));
                }
            }
        }
    }
}
inline void getans(int x, int y, int s) {
    ans[x][y] = 1;
    pa t = from[x][y][s]; 
    if(t == pa(-1, -1, -1)) return ;
    getans(t.i, t.j, t.s);
    if(t.i==x && t.j==y) getans(t.i, t.j, s-t.s);
}

int main() {
    for (int i=1; i<=10; ++i)
        for (int j=1; j<=10; ++j)
            for (int k=0; k<=1025; ++k) f[i][j][k] = 1e9, from[i][j][k] = pa(-1, -1, -1); 
    scanf("%d%d", &n, &m);
    for (int i=1; i<=n; ++i)
        for (int j=1; j<=m; ++j) {
            f[i][j][0] = mp[i][j];
            scanf("%d", &mp[i][j]); 
            if(mp[i][j] == 0)
                f[i][j][1<<k] = 0, st[i][j] = (1<<k), ++k; 
        }
    int status_size = (1<<k)-1;
    for (int status=1; status<=status_size; ++status) {
        for (int i=1; i<=n; ++i)
            for (int j=1; j<=m; ++j) { 
                for (int ss = status-1&status; ss; ss=ss-1&status) {
                    if(f[i][j][status] > f[i][j][ss] + f[i][j][status^ss] - mp[i][j]) {
                        f[i][j][status] = f[i][j][ss] + f[i][j][status^ss] - mp[i][j];
                        from[i][j][status] = pa(i, j, ss);
                    }
                }
            }
        spfa(status);
    }
    
    for (int i=1; i<=n; ++i)
        for (int j=1; j<=m; ++j) {
            if(!mp[i][j]) {
                printf("%d\n", f[i][j][status_size]);
                getans(i, j, status_size);
                for (int x=1; x<=n; ++x, puts(""))
                    for (int y=1; y<=m; ++y)
                        if(ans[x][y]) {
                            if(mp[x][y]) printf("o");
                            else printf("x");
                        } else printf("_"); 
                i = n+1, j = m+1;
            }
        }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/galaxies/p/bzoj2595.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值