关闭

hdu3311Dig The Wells

130人阅读 评论(0) 收藏 举报
分类:

链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=3311

题意:n个和尚住的地方,m个其他地方。每个地方挖一口井需要花费q[i]的钱,两个地方连通需要花费对应的边权值。求所有人都能喝到水的最小花费。

分析:斯塔纳树。如hdu4085,只要在加一位二进制表示集合内是否已经有水了即可。详见代码。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<vector>
#include<string>
#include<stdio.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=1010;
const int mod=1000000007;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const int INF=1000000010;
const ll MAX=1ll<<55;
const double eps=1e-5;
const double inf=~0u>>1;
const double pi=acos(-1.0);
typedef double db;
typedef long double ldb;
typedef unsigned int uint;
typedef unsigned long long ull;
queue<int>Q;
int n,m,p,K,T,e[N],q[N];
int dp[N][80],bo[100*N],ans[80];
int tot,u[N],v[10*N],w[10*N],pre[10*N];
inline void add(int a,int b,int c) {
    v[tot]=b;w[tot]=c;pre[tot]=u[a];u[a]=tot++;
    v[tot]=a;w[tot]=c;pre[tot]=u[b];u[b]=tot++;
}
inline void init() {
    int i,j,a,b,c;
    K=n+m;T=1<<n;
    memset(bo,0,sizeof(bo));
    tot=0;memset(u,-1,sizeof(u));
    memset(e,0,sizeof(e));
    for (i=0;i<n;i++) e[i+1]=1<<i;
    for (i=1;i<=K;i++) scanf("%d", &q[i]);
    for (i=1;i<=K;i++)
        for (j=0;j<T*2;j++) dp[i][j]=INF;
    for (i=1;i<=K;i++) dp[i][0]=dp[i][0|e[i]]=0,dp[i][T]=dp[i][T|e[i]]=q[i];
    for (i=1;i<=p;i++) {
        scanf("%d%d%d", &a, &b, &c);add(a,b,c);
    }
}
inline void spfa() {
    int i,x,y,z;
    while (!Q.empty()) {
        x=Q.front();Q.pop();bo[x]=0;
        y=x%100;x/=100;z=y/T;y%=T;
        for (i=u[x];i!=-1;i=pre[i]) {
            if (!z&&dp[v[i]][y]>dp[x][y]+w[i]) {
                dp[v[i]][y]=dp[x][y]+w[i];
                if (!bo[v[i]*100+y]) bo[v[i]*100+y]=1,Q.push(v[i]*100+y);
            }
            if (!z&&dp[v[i]][y|T]>dp[x][y]+w[i]+q[v[i]]) {
                dp[v[i]][y|T]=dp[x][y]+w[i]+q[v[i]];
                if (!bo[v[i]*100+(y|T)]) bo[v[i]*100+(y|T)]=1,Q.push(v[i]*100+(y|T));
            }
            if (z&&dp[v[i]][y|T]>dp[x][y|T]+w[i]) {
                dp[v[i]][y|T]=dp[x][y|T]+w[i];
                if (!bo[v[i]*100+(y|T)]) bo[v[i]*100+(y|T)]=1,Q.push(v[i]*100+(y|T));
            }
        }
    }
}
inline void Steiner_Tree() {
    int i,j,k;
    for (i=0;i<T;i++) {
        for (j=1;j<=K;j++)
            for (k=i;k;k=(k-1)&i) {
                dp[j][i]=min(dp[j][i],dp[j][k]+dp[j][i-k]);
                dp[j][i|T]=min(dp[j][i|T],dp[j][k|T]+dp[j][i-k]);
                dp[j][i|T]=min(dp[j][i|T],dp[j][k]+dp[j][(i-k)|T]);
            }
        for (j=1;j<=K;j++) {
            if (dp[j][i]!=INF) bo[j*100+i]=1,Q.push(j*100+i);
            if (dp[j][i|T]!=INF) bo[j*100+(i|T)]=1,Q.push(j*100+(i|T));
        }
        spfa();
    }
}
void getans() {
    for (int i=0;i<T;i++) ans[i]=INF;
    for (int i=0;i<T;i++)
        for (int j=1;j<=K;j++) ans[i]=min(ans[i],dp[j][i|T]);
    for (int i=0;i<T;i++)
        for (int j=i;j;j=(j-1)&i) ans[i]=min(ans[i],ans[j]+ans[i-j]);
    printf("%d\n", ans[T-1]);
}
int main()
{
    while (scanf("%d%d%d", &n, &m, &p)!=EOF) {
        init();
        Steiner_Tree();
        getans();
    }
    return 0;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:80383次
    • 积分:4147
    • 等级:
    • 排名:第7395名
    • 原创:348篇
    • 转载:0篇
    • 译文:0篇
    • 评论:20条
    文章分类
    最新评论