Travelling HDU - 3001

题目链接:Travelling HDU - 3001

===================================================

Travelling

Time Limit: 3000MS
Memory Limit: 32768 kB

Description

After coding so many days,Mr Acmer wants to have a good rest.So travelling is the best choice!He has decided to visit n cities(he insists on seeing all the cities!And he does not mind which city being his start station because superman can bring him to any city at first but only once.), and of course there are m roads here,following a fee as usual.But Mr Acmer gets bored so easily that he doesn’t want to visit a city more than twice!And he is so mean that he wants to minimize the total fee!He is lazy you see.So he turns to you for help.

Input

There are several test cases,the first line is two intergers n(1<=n<=10) and m,which means he needs to visit n cities and there are m roads he can choose,then m lines follow,each line will include three intergers a,b and c(1<=a,b<=n),means there is a road between a and b and the cost is of course c.Input to the End Of File.

Output

Output the minimum fee that he should pay,or -1 if he can’t find such a route.

Sample Input

2 1
1 2 100
3 2
1 2 40
2 3 50
3 3
1 2 3
1 3 4
2 3 10

Sample Output

100
90
7

===================================================

算法:3进制状态压缩+dp/bfs

难点:

  • 题意理解:

But Mr Acmer gets bored so easily that he doesn’t want to visit a city more than twice!

由于这里我看太快了,意思为不能。。2次,误解为只能经过一次,然后就感觉简单,dfs+剪枝然后一直wrong。

后面看了看,原来是不能超过2次。所以状态为0,1,2.即经过的次数。用3进制进行状态压缩。

愿意就是acmer可以为了达到经过所有地点并且路费最低,允许他经过一个点不超过2次。

  • 在于3进制预处理。枚举所有可能的状态,然后用数组存储各种状态下点的经过数。
  • 状态最优更新:

dp[i][j]: i为3进制状态,j为最后的连接点。

1.如果状态i,检测出所有点是否经过,然后进行答案更新

2.通过遍历点j的边进行向下寻找符合进入的点,进行dp数组更新

===================================================

1.bfs(更加易懂)

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int INF = 0x3f3f3f3f;

int n,m,ans;
int path[11][11];
int state[65432][11],three[11];
//three[i]表示3的i次方,state[i][j]表示3进制状态中j位的情况(情况分别为0,1,2,代表一个点经过数)
int dp[65432][11];
//dp[i][j]3进制状态下情况以j点作为最后连接点的最优即最少代价
//(状态下一步:找经过数为0或1的点,然后进行向下添加状态,最优则更新;)

void init(){//
    three[0] = 1;
    for(int i=1;i<11;i++) three[i] = three[i-1]*3;
    for(int i=0;i<three[10];i++){
        int tmp = i;
        for(int j=0;j<10;j++) state[i][j] = tmp%3,tmp/=3;
    }
}

struct node{
    int stat,now,free;
    node (int a,int b,int c):stat(a),now(b),free(c) {}
    bool check(){
        for(int i=0;i<n;i++) if(state[stat][i]==0) return false;
        return true;
    }
};

void bfs(int x){
    queue<node> q;
    dp[three[x]][x] = 0;
    q.push(node(three[x],x,0));
    while(!q.empty()){
        node p = q.front();q.pop();
        if(p.check()) {ans = min(ans,p.free);continue;}
        if(p.free >= ans) continue;//剪枝
        for(int i=0;i<n;i++){
            if(p.now==i || state[p.stat][i]>1 || path[p.now][i]==INF) continue;
            int cost = p.free + path[p.now][i];
            if(cost >= ans) continue;//剪枝
            if(dp[p.stat+three[i]][i]!=INF&&dp[p.stat+three[i]][i]<=cost) continue;
            //这里dp函数作用在于判重并且是否最优,没可能最优肯定不能入队
            dp[p.stat+three[i]][i] = cost;
            q.push(node(p.stat+three[i],i,cost));
        }
    }
}

void solve(){
    memset(dp,INF,sizeof dp);
    for(int i=0;i<n;i++) bfs(i);
}

int main()
{
    init();
    while(cin>>n>>m){
        memset(path,INF,sizeof path);
        for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;path[a-1][b-1] = min(path[a-1][b-1],c);path[b-1][a-1] = path[a-1][b-1];}
        ans = INF;
        solve();
        if(ans==INF) puts("-1");
        else cout<<ans<<endl;
    }
    return 0;
}

===================================================

2.状态dp(其实与bfs无异,通过3循环遍历,一循环状态i,二循环尾点j,三循环进行下一连接点k查找)

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;

const int INF = 0x3f3f3f3f;

int n,m,ans;
int path[11][11];
int state[65432][11],three[11];
//three[i]表示3的i次方,state[i][j]表示3进制状态中j位的情况(情况分别为0,1,2,代表一个点经过数)
int dp[65432][11];
//dp[i][j]3进制状态下情况以j点作为最后连接点的最优即最少代价
//(状态下一步:找连接数为0或1的点,然后进行向下添加状态,最优则更新;)

void init(){//
    three[0] = 1;
    for(int i=1;i<11;i++) three[i] = three[i-1]*3;
    for(int i=0;i<three[10];i++){
        int tmp = i;
        for(int j=0;j<10;j++) state[i][j] = tmp%3,tmp/=3;
    }
}

void solve(){
    memset(dp,INF,sizeof dp);
    //点自己到自己代价为0
    for(int i=0;i<n;i++) dp[three[i]][i] = 0;
    //从状态0开始向后更新,后面状态无法影响前面状态,
    //所以对于全联通的状态进行0~n-1结尾进行最终答案更新就行了
    for(int i=0;i<three[n];i++){
        bool flag = true;
        for(int j=0;j<n;j++){
            if(state[i][j]==0) {flag = false;continue;}
            //if(state[i][j]==2) continue;
            for(int k=0;k<n;k++){
                if(path[j][k]==INF||k==j||state[i][k]==2) continue;
                if(dp[i+three[k]][k] <= dp[i][j]+path[j][k]) continue;
                dp[i+three[k]][k] = dp[i][j]+path[j][k];
            }
        }
        if(flag) {for(int j=0;j<n;j++) ans = min(ans,dp[i][j]);}
    }
}

int main()
{
    init();
    while(cin>>n>>m){
        memset(path,INF,sizeof path);
        for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;path[a-1][b-1] = min(path[a-1][b-1],c);path[b-1][a-1] = path[a-1][b-1];}
        ans = INF;
        solve();
        if(ans==INF) puts("-1");
        else cout<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盐太郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值