poj3123

题意:

给定n个城市和m条可选择修建的道路

下面n行给出每个城市的名字

下面m行给出每条道路及修建该道路的花费。

下面4行,每行给出一对城市。

目标:使得每对城市连通(不同对之间可以不连通)所需要修建的最小花费。

数据保证存在可行解


思路:

首先如果这个问题问的是所有城市都连通,就是一个最小生成树的问题。这里就相当于多个最小生成树的 问题。

图里我们只关心最后的4行8个点,所以我们状压这8个点。这里把(1,5)(2,6)(3,7)(4,8)配对,即重新复制了hash1的值,便于处理

设dp[mask][i] 表示以i为根 ,mask为在 i 的子树里的点集的最小花费。(注意i这个点是不一定出现在j里的,因为i点可以是除这8个点之外的点)

过程中用spfa求最短路

转移:

1、从2个点集合并得到

dp[mask][i] = min( dp[ mask1 ][i]+dp[ mask2 ][i])

其中mask1 & mask2 = 1<<i,mask1 | mask2 = mask

即我们把 根为i ,点集为mask1 的子树和根同样为i 点集为mask2的子树的花费和 就是根为i ,点集为 mask = mask1+mask2 的一个花费,取个最小即可。

2、把i点加到一个子树上得到。

我们枚举这个子树,显然这个子树的点集就是mask,当然根是不确定的,因为根不一定是j中的点,所以枚举根K。

这样我们就能得到子树的状态是 dp[mask][K] , 而加入i点时 可以和树上任意一个点相连
 
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
const int maxn=30;
map<string,int>hsh;
queue<int>q;
bool vis[maxn];
int hash1[maxn],dp[1<<8][maxn],dis[maxn][maxn],num[maxn],ans[1<<4];

int main(){
    int n,m;
    while(scanf("%d%d",&n,&m),n || m){
        memset(dp,-1,sizeof dp);
        memset(dis,-1,sizeof dis);
        memset(ans,-1,sizeof ans);
        memset(hash1,-1,sizeof hash1);
        hsh.clear();
        for(int i=0;i<n;i++){string s;cin>>s;hsh[s]=i;dis[i][i]=0;dp[0][i]=0;}
        while(m--){string a,b;int c;cin>>a>>b>>c;int x=hsh[a],y=hsh[b];dis[x][y]=dis[y][x]=dis[x][y]==-1?c:min(dis[x][y],c);}
        for(int i=0;i<8;i++){
            string s;cin>>s;
            hash1[hsh[s]]=((i&1)<<2)|(i>>1);
            dp[1<<hash1[hsh[s]]][hsh[s]]=0;
            dp[0][hsh[s]]=-1;
            num[hash1[hsh[s]]]=hsh[s];     
        }
        for(int mask=0;mask< 1<<8;mask++){
            for(int i=0;i<n;i++){
                if(hash1[i]!=-1 && !(mask>>hash1[i]&1))continue;
                for(int mask1=(mask-1)&mask;mask1;mask1=(mask1-1)&mask){
                    int mask2=mask^mask1 |((hash1[i]!=-1)?1<<hash1[i]:0);
                    if(dp[mask1][i]!=-1 && dp[mask2][i]!=-1)dp[mask][i]=dp[mask][i]==-1?dp[mask2][i]+dp[mask1][i]:min(dp[mask][i],dp[mask2][i]+dp[mask1][i]);
                }
                while(!q.empty())q.pop();
                for(int i=0;i<n;i++)if(dp[mask][i]!=-1){q.push(i);vis[i]=true;}else vis[i]=false;
                while(!q.empty()){
                    int u=q.front();q.pop();vis[u]=false;
                    for(int v=0;v<n;v++)
                        if(dis[u][v]!=-1){
                            if(dp[mask][v]==-1 || dp[mask][v]>dp[mask][u]+dis[u][v]){
                                dp[mask][v]=dp[mask][u]+dis[u][v];
                                if(!vis[v]){q.push(v);vis[v]=true;}
                            }
                        }
                }
            }
            for(int mask=0;mask< 1<<4;mask++){
                for(int i=0;i<4;i++)
                    if(mask>>i & 1){ans[mask]=dp[mask<<4 | mask][num[i]];break;}
                for(int mask1=(mask-1)&mask;mask1;mask1=(mask1-1)&mask){
                    int mask2=mask^mask1;
                    if(ans[mask1]!=-1 && ans[mask2]!=-1)ans[mask]=ans[mask]==-1?(ans[mask1]+ans[mask2]):min(ans[mask],ans[mask1]+ans[mask2]);
                }
            }
        }
        printf("%d\n",ans[(1<<4)-1]);
    }
    return 0;
}

 

posted @ 2018-06-11 20:21  lnyzo  阅读(329)  评论(0)    收藏  举报