(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦
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[sta⨁x][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 < = i < = n ) \sum{C(i,n)*2^i} (1<=i<=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)
k≤5)个点和后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
<
=
n
<
=
5
)
n(1<=n<=5)
n(1<=n<=5)个和尚,每个和尚位于一个寺庙内,
m
(
0
<
=
m
<
=
1
e
3
)
m(0<=m<=1e3)
m(0<=m<=1e3)个其他地方。然后
n
+
m
n+m
n+m个数,为在第
i
i
i个地方打井所需的代价。以下
p
(
0
<
=
p
<
=
5
e
3
)
p(0<=p<=5e3)
p(0<=p<=5e3)行,为在标号
u
u
u和标号
v
v
v之间建边的代价
w
w
w。
求最小代价和,使得所有和尚都能喝到水
题解
- 建立虚拟节点0,所有节点和0连边,边权为该点点权,然后跑一遍斯坦纳树即可。答案为 d p [ ( 1 < < ( n + 1 ) ) − 1 ] [ 0 ] dp[(1<<(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
*/