hdu 4640 Island and study-sister 13多校,难

题意:一个acm队伍,三个人,然后有个n(最多17个)个岛组成的地方,有m条路把这些岛联系在一起,每个路有个权值,表示时间。然后三个人从1点出发,分别到这些岛上去,三个人去的地方不能交叉,也就是说一个人去过该点,另一个人就不能在这个点过了。如果有个点去不了那么就输出-1.然后输出最短的时间。
思路:对这17个岛用二进制表示。
int f[1<<18][20];//1个人 i状态走到j的最小值
int dp[4][1<<18];//i个人 状态j 的最小值
那么利用spfa求最短路,存入f中。
然后再枚举2个人,3个人的情况求dp;
dp[i][mask]表示i个人在状态mask下的最小值,那么枚举mask的子集v,前者就可以由dp[1][v]+dp[i-1][mask^v]得来,
设s是最终的状态,然后枚举mask,当mask包含s时,算一次最小值即可。
http://www.cnblogs.com/wally/p/3330414.html
代码是网上大牛的,厚着脸搞过来:
#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define inf 1<<30
using namespace std;
typedef pair<int,int> pp;
int map[20][20],n,m,s,ans;
int f[1<<18][20];//1个人 i状态走到j min
int dp[4][1<<18];//i个人 状态j min
bool mark[1<<18][20];//用来标记i状态走到了j

void bfs()
{
    queue<pp>que;
    que.push(make_pair(0,0));
    memset(mark,false,sizeof(mark));
    for(int i=0; i<(1<<n); i++)
        for(int j=0; j<n; j++)
            f[i][j]=inf;
    f[0][0]=0;
    while(!que.empty())
    {
        pp tmp=que.front();
        que.pop();
        int st=tmp.first,ed=tmp.second;
        mark[st][ed]=false; 
        for(int i=0; i<n; i++)
        {
            if(map[ed][i]<inf&&f[st|(1<<i)][i]>f[st][ed]+map[ed][i])
            {
                f[st|(1<<i)][i]=f[st][ed]+map[ed][i];
                if(!mark[st|(1<<i)][i])
                {
                    mark[st|(1<<i)][i]=true;
                    que.push(make_pair(st|(1<<i),i));
                }
            }
        }
    }
}

void solve()
{
    for(int i=1; i<=3; i++)
        for(int j=0; j<(1<<n); j++)
            dp[i][j]=inf;
    for(int i=0; i<(1<<n); i++)
        for(int j=0; j<n; j++)
            dp[1][i]=min(dp[1][i],f[i][j]);//只要看i就可以,i表示到过哪些地方,j只是表示终点在哪里
    for(int i=2; i<=3; i++)//枚举人数
        for(int j=0; j<(1<<n); j++)//这些个状态
            for(int k=j; k; k=(k-1)&j) //枚举子集
                dp[i][j]=min(dp[i][j],max(dp[1][k],dp[i-1][(j^k)]));
    ans=inf;
    for(int i=1; i<=3; i++)
        for(int j=0; j<(1<<n); j++)
            if((j&s)==s)//看s是否是j的子集,包括s本身,因为j如果去得地方更多反而时间更短也有可能,注意。
                ans=min(ans,dp[i][j]);
    ans=ans==inf?-1:ans;
    printf("%d\n",ans);
}

int main()
{
    //freopen("in.txt","r",stdin);
    int t,cas,i,j,k,u,v,w;
    scanf("%d",&t);
    for(cas=1; cas<=t; cas++)
    {
        scanf("%d%d",&n,&m);
        for(i=0; i<=n; i++) for(j=0; j<=n; j++) map[i][j]=i==j?0:inf;
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&w);
            --u;
            --v;
            map[u][v]=map[v][u]=min(map[u][v],w);
        }
        s=0;
        scanf("%d",&k);
        while(k--)
        {
            scanf("%d",&u);
            s|=(1<<(u-1));
        }
        bfs();
        printf("Case %d: ",cas);
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值