斯坦纳树 - 2019南昌邀请赛A,HDU4085,ZOJ3613,hdu3311,WC2008游览计划 输出方案

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

Catalog

斯坦纳树?

d p [ s t a ] [ i ] dp[sta][i] dp[sta][i]表示以 i i i为根,指定集合中的点的连通状态为 s t a sta sta的生成树的最小总权值。
i i i为根, i i i只是一个表示,为了转移的时候方便。
转移1:先通过连通状态的子集进行转移
d p [ s t a ] [ i ] = s m l ( d p [ x ] [ i ] + d p [ s t a ⨁ x ] [ i ] − v a l [ i ] ) dp[sta][i] = sml(dp[x][i] + dp[sta \bigoplus x][i] - val[i]) dp[sta][i]=sml(dp[x][i]+dp[stax][i]val[i])
转移2:在当前枚举的连通状态下,对该连通状态进行松弛操作
d p [ s t a ] [ i ] = s m l ( d p [ s t a ] [ j ] + w [ i ] [ j ] ) dp[sta][i] = sml(dp[sta][j] + w[i][j]) dp[sta][i]=sml(dp[sta][j]+w[i][j])


为什么只需对该连通状态进行松弛?因为更后面的连通状态会由先前的连通状态通过第一重转移得到,所以无需对别的连通状态松弛。松弛操作用SPFA即可。这种方法复杂度: O ( n × 3 k + c E × 2 k ) O(n\times 3^k+cE\times 2^k) O(n×3k+cE×2k)

c c c为SPFA复杂度中的常数, E E E为边的数量,但几乎达不到全部边的数量,甚至非常小。 3 k 3^k 3k来自于子集的转移 ∑ C ( i , n ) ∗ 2 i ( 1 &lt; = i &lt; = n ) \sum{C(i,n)*2^i} (1&lt;=i&lt;=n) C(i,n)2i(1<=i<=n),用二项式展开求一下和。


当然可以用Dijkstra代替SPFA,下面第一道题我用的是Dijkstra松弛最短路径。
用Dijkstra的好处是复杂度更加稳定。
复杂度: O ( n × 3 k + E × l o g ( n ) × 2 k ) O(n\times 3^k+E\times log(n)\times 2^k) O(n×3k+E×log(n)×2k)

虽然感觉有一点奇怪,但是他的这种处理好像刚好不会出任何问题。就是有这种可能, d p [ s t a ] [ i ] dp[sta][i] dp[sta][i]的值是包含了 s t a sta sta状态和点 i i i,不过 s t a sta sta里面会并不包含点 i i i

2019南昌邀请赛A,HDU4085

2019南昌邀请赛A题HDU4085
这两题可以说几乎一样了,hdu4085只要求前k( k ≤ 5 ) k\le 5) k5)个点和后k个点两两之间一一对应,但并没有固定谁该和谁对应。
邀请赛A题给定4对点,固定两两之间应该联通。写法差不多,改一下 c h e c k ( ) check() check()状态的函数即可,合法状态要么不包含给定点对,要么给定点对的两个点都包含。
注意有一个坑点是4对点中可能有重点。

/*
枚举状态sta时,遇到在第一类转移之后有更新的节点,将其加入队列,用spfa做一次迭代更新。普通的转
 移是不能保证最优性的,而spfa采用迭代逼近的方式,将更新过的点再进行更新,可以保证最后每个节点
 都是“最短路”。

对于ZOJ WormHole Transportation 和 HDU Peach Blossom Spring,其解为一片斯坦纳生成
 森林,用dp[sta]数组来维护最优解,可利用f[i][sta]来松弛dp[sta],得到当前状态的最小代价。
 最后由小到大枚举状态,将森林在0代价的条件下合并成“生成树”即可。
 注意在这过程中要过滤掉非法状态。
 */
const int MXN = 1e3 + 7;
const int MXE = 1e4 + 7;
int n, m, stnum;
int head[MXN], tot;
struct lp {
    int v, nex;
    int w;
    friend bool operator <(const lp&a, const lp&b) {
        return a.w > b.w;
    }
}cw[MXE], A, B;
LL dp[(1<<10) + 5][35], val[MXN], f[(1<<10)+5];
int in[MXN];
priority_queue<lp> Q;
string a, b, aa[4], bb[4];
int X[4], Y[4], to[MXN], re[MXN];
map<string, int> mp, mp2;
void add_edge(int u, int v, int w) {
    cw[++tot].v = v, cw[tot].nex = head[u], cw[tot].w = w;
    head[u] = tot;
    cw[++tot].v = u, cw[tot].nex = head[v], cw[tot].w = w;
    head[v] = tot;
}
void spfa(int S) {
    while(!Q.empty()) {
        A = Q.top(); Q.pop();
        int u = A.v;
        if(in[u]) continue;
        in[u] = 1;
        for(int i = head[u], v; ~i; i = cw[i].nex) {
            v = cw[i].v;
            if(dp[S][v] > dp[S][u] + cw[i].w) {
                dp[S][v] = dp[S][u] + cw[i].w;
                B.v = v, B.w = dp[S][v];
                Q.push(B);
            }
        }
    }
}
void steiner() {
    clr(dp, 0x3f);
    for(int i = 0; i < stnum; ++i) {
        dp[1<<i][re[i]] = 0;
    }
    int sta = 1 << stnum;
    for (int S = 1; S < sta; ++S) {
        for (int x = S; x; x = (x-1)&S) {
            for(int i = 0; i < n; ++i) {
                dp[S][i] = sml(dp[S][i], dp[x][i] + dp[S^x][i]);
            }
        }
        for(int i = 0; i < n; ++i) {
            in[i] = 0;
            if(dp[S][i] != INFLL) {
                A.v = i, A.w = dp[S][i];
                Q.push(A);
            }
        }
        spfa(S);
    }
}
bool check(int s) {
    bool flag = true;
    for(int i = 0; i < 4; ++i) {
        if(((s>>to[X[i]])&1) != ((s>>to[Y[i]])&1)) flag = false;
    }
    return flag;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
    //freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
    tot = -1;
    cin >> n >> m;
    for(int i = 0; i < n; ++i) {
        cin >> a;
        mp[a] = i;
        head[i] = -1;
    }
    for(int i = 1, x; i <= m; ++i) {
        cin >> a >> b >> x;
        add_edge(mp[a], mp[b], x);
    }
    for(int i = 0; i < 4; ++i) {
        cin >> aa[i] >> bb[i];
        X[i] = mp[aa[i]], Y[i] = mp[bb[i]];
        if(mp2[aa[i]] == 0) to[X[i]] = stnum ++, re[stnum - 1] = X[i];
        if(mp2[bb[i]] == 0) to[Y[i]] = stnum ++, re[stnum - 1] = Y[i];
        mp2[aa[i]] = mp2[bb[i]] = 1;
    }
    steiner();
    int sta = 1 << stnum;
    clr(f, 0x3f);
    for(int i = 1; i < sta; ++i) {
        for(int j = 0; j < n; ++j) f[i] = sml(f[i], dp[i][j]);
    }
//    for(int i = 1; i < sta; ++ i) printf("%d ", f[i]); printf("\n");
    for(int i = 1; i < sta; ++i) {
        if(check(i)) {
            for (int x = i; x; x = (x-1)&i) {
                if(check(x)) f[i] = sml(f[i], f[x] + f[i ^ x]);
            }
        }
    }
    cout << f[sta - 1] << endl;
#ifndef ONLINE_JUDGE
    cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
    return 0;
}

ZOJ 3613

z o j zoj zoj已经迁移到 p a t pat pat上去了。。

const int MXN = 1e3 + 7;
const int MXE = 1e4 + 7;
int n, m;
int head[MXN], tot;
struct lp {
    int v, nex;
    int w;
} cw[MXE];
queue<int> Q;
int dp[(1 << 10) + 5][205], val[MXN], f[(1 << 10) + 5];
int in[MXN], P[MXN], T[MXN], pnum[MXN], re[MXN], stnum;
void add_edge(int u, int v, int w) {
    cw[++tot].v = v, cw[tot].nex = head[u], cw[tot].w = w;
    head[u] = tot;
    cw[++tot].v = u, cw[tot].nex = head[v], cw[tot].w = w;
    head[v] = tot;
}
void spfa(int S) {
    while(!Q.empty()) {
        int u = Q.front();
        Q.pop();
        in[u] = 0;
        for(int i = head[u], v; ~i; i = cw[i].nex) {
            v = cw[i].v;
            if(dp[S][v] > dp[S][u] + cw[i].w) {
                dp[S][v] = dp[S][u] + cw[i].w;
                if(!in[v]) Q.push(v), in[v] = 1;
            }
        }
    }
}
void steiner() {
    clr(dp, 0x3f);
    for(int i = 0; i < stnum; ++i) {
        dp[1 << i][re[i]] = 0;
    }
    int sta = 1 << stnum;
    for (int S = 1; S < sta; ++S) {
        for (int x = S; x; x = (x - 1)&S) {
            for(int i = 0; i < n; ++i) {
                dp[S][i] = sml(dp[S][i], dp[x][i] + dp[S ^ x][i]);
            }
        }
        for(int i = 0; i < n; ++i) {
            in[i] = 0;
            if(dp[S][i] != INF && !in[i]) {
                Q.push(i), in[i] = 1;
            }
        }
        spfa(S);
    }
}
bool check(int s) {
    int cnt = 0;
    for(int i = 0; i < stnum; ++i) if(s & (1 << i)) cnt += pnum[i];
    return cnt >= 0;
}
int get(int s) {
    int cnt = 0, cnt2 = 0;
    for(int i = 0; i < stnum; ++i) {
        if(s & (1 << i)) {
            if (pnum[i] >= 0) cnt += pnum[i];
            else cnt2 -= pnum[i];
        }
    }
    assert(cnt >= cnt2);
    return cnt2;
}
int main() {
    while(~scanf("%d", &n)) {
        tot = -1;
        stnum = 0;
        int gg = 0;
        for(int i = 0; i < n; ++i) {
            P[i] = read(), T[i] = read();
            if(P[i] && T[i]) -- P[i], -- T[i], ++ gg;
            if(P[i] || T[i]) {
                pnum[stnum] = P[i] - T[i];
                re[stnum] = i;
                ++ stnum;
            }
            head[i] = -1;
        }
        m = read();
        for(int i = 1, u, v, w; i <= m; ++i) {
            u = read(), v = read(), w = read();
            add_edge(u - 1, v - 1, w);
        }
        steiner();
        int sta = 1 << stnum;
        clr(f, 0x3f);
        for(int i = 0; i < sta; ++i) {
            for(int j = 0; j < n; ++j) f[i] = sml(f[i], dp[i][j]);
        }
        //for(int i = 1; i < sta; ++ i) printf("%d ", f[i]); printf("\n");
        int Maxnum = 0, Mincost = 0;
        for(int i = 1; i < sta; ++i) {
            if(check(i)) {
                for (int x = i & (i - 1); x; x = (x - 1)&i) {
                    if(check(x) && check(i ^ x)) {
                        // debug(i, x, i ^ x, f[i], f[x], f[i^x])
                        f[i] = sml(f[i], f[x] + f[i ^ x]);
                    }
                }
                if(f[i] == INF) continue;
                int num = get(i);
                // debug(i, f[i], num)
                if(num > Maxnum) {
                    Maxnum = num;
                    Mincost = f[i];
                } else if(num == Maxnum && f[i] < Mincost) {
                    Mincost = f[i];
                }
            }
        }
        printf("%d %d\n", Maxnum + gg, Mincost);
    }
#ifndef ONLINE_JUDGE
    cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "ms." << endl;
#endif
    return 0;
}

HDU 3311 将点权转换为边权

n ( 1 &lt; = n &lt; = 5 ) n(1&lt;=n&lt;=5) n(1<=n<=5)个和尚,每个和尚位于一个寺庙内, m ( 0 &lt; = m &lt; = 1 e 3 ) m(0&lt;=m&lt;=1e3) m(0<=m<=1e3)个其他地方。然后 n + m n+m n+m个数,为在第 i i i个地方打井所需的代价。以下 p ( 0 &lt; = p &lt; = 5 e 3 ) p(0&lt;=p&lt;=5e3) p(0<=p<=5e3)行,为在标号 u u u和标号 v v v之间建边的代价 w w w
求最小代价和,使得所有和尚都能喝到水

题解

  • 建立虚拟节点0,所有节点和0连边,边权为该点点权,然后跑一遍斯坦纳树即可。答案为 d p [ ( 1 &lt; &lt; ( n + 1 ) ) − 1 ] [ 0 ] dp[(1&lt;&lt;(n+1))-1][0] dp[(1<<(n+1))1][0]

WC2008 游览计划

二维和一维空间其实没啥区别吧,把二维变成一维点即可。建图的话相邻两个点连一条以终点点权的为边权的单向边即可。
输出方案就记录一个前驱,最后搜索一遍就行了。

const int MXN = 1e3 + 17;
const int MXE = 2e4 + 7;
int n, m, q;
int head[MXN], tot;
struct lp {
    int v, nex;
    int w;
}cw[MXE];
int dp[(1<<10) + 5][MXN], val[MXN], f[(1<<12)+5];
int in[MXN], mp[15][15], vis[15][15];
pii pre[(1<<10)+5][MXN];
queue<int> Q;
int re[MXN], stnum, all;
vector<int> vs;
void add_edge(int u, int v, int w) {
    cw[++tot].v = v, cw[tot].nex = head[u], cw[tot].w = w;
    head[u] = tot;
}
void spfa(int S) {
    while(!Q.empty()) {
        int u = Q.front(); Q.pop();
        in[u] = 0;
        for(int i = head[u], v; ~i; i = cw[i].nex) {
            v = cw[i].v;
            if(dp[S][v] > dp[S][u] + cw[i].w) {
                dp[S][v] = dp[S][u] + cw[i].w;
                pre[S][v] = mk(S, u);
                if(!in[v]) Q.push(v), in[v] = 1;
            }
        }
    }
}
int GET(int x) {
    return mp[x/m][x%m];
}
void steiner() {
    int sta = 1 << stnum;
    for(int i = 0; i < sta; ++i) {
        for(int j = 0; j < all; ++j) dp[i][j] = INF;
    }
    for(int i = 0; i < stnum; ++i) {
        dp[1<<i][vs[i]] = 0;
    }
    for (int S = 1; S < sta; ++S) {
        for (int x = (S-1)&S; x; x = (x-1)&S) {
            for(int i = 0; i < all; ++i) {
                if(dp[x][i] + dp[(S^x)][i] - GET(i) < dp[S][i]) {
                    dp[S][i] = dp[x][i] + dp[(S^x)][i] - GET(i);
                    pre[S][i] = mk(x, i);
                }
            }
        }
        for(int i = 0; i < all; ++i) {
            in[i] = 0;
            if(dp[S][i] != INF && !in[i]) {
                Q.push(i), in[i] = 1;
            }
        }
        spfa(S);
    }
}
int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
int tim = 1000000;
void dfs(int s, int x) {
    tim --;
    if(tim <= 0) return;
    vis[x/m][x%m] = 1;
    if(pre[s][x].fi == -1 || pre[s][x].se == -1) return;
//    if(pre[s][x].fi == s && pre[s][x].se == x) return;
    dfs(pre[s][x].fi, pre[s][x].se);
    if(pre[s][x].se == x) dfs((s^pre[s][x].fi), x);
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
    //freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
    clr(pre, -1);
    n = read(), m = read();
    all = n * m;
    tot = -1;
    for (int i = 0; i <= all; ++i) head[i] = -1;
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < m; ++j) {
            scanf("%d", &mp[i][j]);
            if(mp[i][j] == 0) {
                vs.eb(i * m + j);
                re[i * m + j] = 1 << stnum;
                stnum ++;
            }
        }
    }
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < m; ++j) {
            for(int h = 0; h < 4; ++h) {
                int x = i + dir[h][0], y = j + dir[h][1];
                if(x < 0 || y < 0 || x >= n || y >= m) continue;
                add_edge(i * m + j, x * m + y, mp[x][y]);
//                add_edge( x * m + y, i * m + j, mp[i][j]);
            }
        }
    }
    steiner();
    dfs((1<<stnum)-1, vs[0]);
    printf("%d\n", dp[(1 << stnum) - 1][vs[0]]);
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < m; ++j) {
            if(mp[i][j] == 0) putchar('x');
            else if(vis[i][j]) putchar('o');
            else putchar('_');
        }
        putchar('\n');
    }
    return 0;
}
头文件
#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define fi first
#define se second
#define endl '\n'
#define o2(x) (x)*(x)
#define BASE_MAX 62
#define mk make_pair
#define eb emplace_back
#define SZ(x) ((int)(x).size())
#define all(x) (x).begin(), (x).end()
#define clr(a,b) memset((a),(b),sizeof((a)))
#define iis std::ios::sync_with_stdio(false); cin.tie(0)
#define my_unique(x) sort(a(x)),x.erase(unique(a(x)),x.end())
using namespace std;
#pragma optimize("-O3")
typedef long long LL;
typedef pair<int, int> pii;
inline LL read(){
    LL x=0;int f=0;char ch=getchar();
    while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x=f?-x:x;
}
inline void write(LL x) {
    if(x==0){putchar('0'),putchar('\n');return;}
    if(x < 0) {putchar('-');x=-x;}
    static char s[23];int l = 0;
    while(x!=0)s[l++]=x%10+48,x/=10;
    while(l)putchar(s[--l]);
    putchar('\n');
}
int lowbit(int x) {return x&(-x);}
template<class T> T big(const T& a1,const T& a2) { return a1>a2?a1:a2; }
template<typename T, typename ...R> T big (const T& f,const R& ...r) { return big(f, big (r...)); }
template<class T> T sml(const T& a1,const T& a2) { return a1<a2?a1:a2; }
template<typename T, typename ...R> T sml (const T& f,const R& ...r) { return sml(f, sml (r...)); }
void debug_out() { cerr << '\n'; }
template<typename T, typename ...R> void debug_out (const T& f,const R& ...r) { cerr << f << " "; debug_out (r...); }
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);

const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000009, 1004535809};
const LL BASE[] = {1572872831, 1971536491};
const int mod = 1e9 + 6;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int MXN = 2e6 + 7;

int main() {
#ifndef ONLINE_JUDGE
    freopen("E://ADpan//in.in", "r", stdin);
    //freopen("E://ADpan//out.out", "w", stdout);
#endif
    n = read(); m = read();
    debug(big(n, m));
#ifndef ONLINE_JUDGE
    cout << "time cost:" << 1.0*clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
    return 0;
}


/*
clock_t a;
int t = GetTickCount();
#ifndef ONLINE_JUDGE
  cout << "time cost:" << clock() << "ms" << endl;
#endif
*/
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值