您的位置:首页 > 其它

About Steiner Tree

2016-07-22 20:21 288 查看
昨天刚学了一个叫Steiner Tree的东西 .

今天刚开博客就写出来吧 !

Steiner Tree 问题是对于给定的带权图G,选择其中的一些边W,使其中的一些点联通,并使sigma W最小 .

我们设 f[i]][j] 表示当前位置在 i , 已经经过的点集为 j 的最小花费 .

那么,有两种转移方式 :

f[i][j]=min(f[i][mask1]+f[i][mask2]),(j,mask1,mask2是包含i的集合,并且mask1+mask2=j,这里i重复)

f[i][j]=min(f[v][mask]+w[i][v]),(v是与i相邻的点)

对于第一种,我们枚举集合j,再枚举j的子集mask1,所以mask2=j-mask1

对于第二种,我们可以用SPFA来解决 .

那么,先枚举集合,在对每个集合跑一边当前集合的SPFA , 就可以求出答案了 .

hdu4085 : 答案可能由几个连通块组成,要用dp将答案整合 .

po个代码 :

#include <queue>
#include <cstdio>
using namespace std;
const int oo=(1<<30)-1;
int n,m,k;
int mkw[2050],dp[2050],f[2050][2050];
struct edge {
int u,v,w;
edge *nxt;
edge (int u,int v,int w,edge *pre): u(u),v(v),w(w){nxt=pre;}
};
edge *he[2050];
queue <int> q;
bool inq[2050];
bool upd(int &n1,int n2) {
if (n1>n2) return n1=n2,1; return 0;
}
void SPFA(int mask) {
while (!q.empty()) {
int u=q.front(); q.pop(); inq[u]=0;
for (edge *i=he[u];i;i=i->nxt)
if (upd(f[i->v][mask|mkw[i->v]],f[u][mask]+i->w) && mask==mask|mkw[i->v] && !inq[i->v]) q.push(i->v),inq[i->v]=1;
}
}
void Steiner() {
int i,j,mask;
for (i=1;i<1<<(2*k);i++) {
for (j=1;j<=n;j++) {
for (mask=(i-1)&i;mask;mask=(mask-1)&i)
upd(f[j][i|mkw[j]],f[j][mask|mkw[j]]+f[j][(i-mask)|mkw[j]]);
if (f[j][i|mkw[j]]<oo && !inq[j])
q.push(j),inq[j]=1;
}
SPFA(i);
}
}
bool check(int x){
int r=0;
for(int i=0;x;i++,x>>=1)
r+=(x&1)*(i<k?1:-1);
return r==0;
}
int main() {
int T,i,j,a,b,c;
scanf("%d",&T);
n=m=0;
while (T--) {
for (i=1;i<=n;i++) mkw[i]=0;
for (i=1;i<=n;i++) he[i]=NULL;
scanf("%d%d%d",&n,&m,&k);
for (i=1;i<=m;i++) {
scanf("%d%d%d",&a,&b,&c);
he[a]=new edge(a,b,c,he[a]);
he[b]=new edge(b,a,c,he[b]);
}
for (i=1;i<=n;i++)
for (j=0;j<1<<(2*k);j++) f[i][j]=oo;
for (i=1;i<=k;i++) {
mkw[i]=1<<(i-1);
mkw[n-k+i]=1<<(k+i-1);
f[i][mkw[i]]=0;
f[n-k+i][mkw[n-k+i]]=0;
}
Steiner();

for(j=0;j<1<<(2*k);j++){
dp[j]=oo;
for(i=1;i<=n;i++) dp[j]=min(dp[j],f[i][j|mkw[i]]);
}
for(i=1;i<1<<(2*k);i++)
if(check(i))
for(j=i&(i-1);j;j=(j-1)&i)
if(check(j))
dp[i]=min(dp[i],dp[j]+dp[i-j]);
if(dp[(1<<(2*k))-1]>=oo) printf("No solution\n");
else printf("%d\n",dp[(1<<(2*k))-1]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: