状压dp,三进制,tsp

#include<bits/stdc++.h>//三进制状压dp再做广搜
using namespace std;
const int INF=1e9;
const int maxn=1e5;
int n;
int dp[maxn][13];
int dis[13][13];
int c[11]={1,3,9,27,81,243,729,2187,6561,19683,59049};
int OK[maxn][13];
int NUM[maxn][13];
int a[40][maxn];
int ok(int st,int num)
{
    for(int i=0;i<num;i++,st=st/3) if(!(st%3)) return 0;
    return 1;
}
void init()
{
    for(int i=0;i<c[10];i++)  //预处理走遍j个结点的成功状态
     for(int j=1;j<=10;j++)
        OK[i][j]=ok(i,j);
    memset(NUM,0,sizeof(NUM));
    for(int i=0;i<c[10];i++) //预处理每个状态三进制表示下各个位的值
    {
        int num=1,tmp=i,s=0;
        while(tmp)
        {
            NUM[i][num++]=tmp%3;
            s=s+tmp%3;
            tmp=tmp/3;
        }
        NUM[i][0]=s;
    }
}
int solve()
{
    memset(dp,-1,sizeof(dp));
    for(int i=1;i<=2*n;i++)   //稍做优化
    {
        int num=1;
        for(int j=0;j<c[n];j++) if(NUM[j][0]==i) a[i][num++]=j;
        a[i][0]=num;
    }
       for(int i=1;i<=2*n-1;i++)  //走2n-1步结束
        for(int j1=1;j1<a[i][0];j1++)  //对应合法步数的状态
         {
                int j=a[i][j1];
                for(int k=1;k<=n;k++)
                     if(NUM[j][k]>=1)  //找到已走点 // bug..找了好久(==1)不对
                         for(int p=1;p<=n;p++)
                             if(NUM[j][p]<2&&p!=k&&dis[k][p]!=INF) //找到可走点
                             {
                                 int st=j+c[p-1];
                                 if(i==1) dp[st][p]=dis[k][p];//初始化
                                 else if(dp[j][k]!=-1) //暴力时有些点p不能到
                                 {
                                     if(dp[st][p]==-1) dp[st][p]=dp[j][k]+dis[k][p];//初始化
                                     else dp[st][p]=min(dp[st][p],dp[j][k]+dis[k][p]);
                                     //1001/0101->1101都至dp[st][p];关键点 决定dp,st+一维
                                 }                                                    
                             }
         }
    int ans=INF;
    for(int i=0;i<c[n];i++)
     if(OK[i][n])
      for(int j=1;j<=n;j++)
       if(dp[i][j]!=-1) ans=min(ans,dp[i][j]);
   return ans;
}
int main()
{
   // freopen("C:/Users/hzy/Desktop/11.txt","r",stdin);
    init();
    int m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++)
         for(int j=1;j<=n;j++)
           if(i==j) dis[i][j]=0;
           else dis[i][j]=INF;
         for(int i=0;i<m;i++)
         {
             int u,v,w;
             scanf("%d%d%d",&u,&v,&w);
             if(dis[u][v]==INF) dis[u][v]=dis[v][u]=w;   //去重边
             else dis[u][v]=dis[v][u]=min(dis[u][v],w);
         }
         int ans=INF;
         ans=solve();
         if(ans==INF) cout<<-1<<endl;
         else   cout<<ans<<endl;
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值