zoukankan      html  css  js  c++  java
  • poj 2404 中国邮递员问题 欧拉回路判定+状压dp

    /*
    状压dp
    邮递员问题:求经过任意点出发经过每一条边一次并回到原点。
    解法:1、如果是欧拉回路那么就是所有的边的总和。
          2、一般的解法,找出所有的奇度顶点,任意两个顶点匹配,即最小完美匹配,可用状压dp。
    */
    #include<stdio.h>
    #include<string.h>
    #define N  20
    #define inf 1000000000
    int ma[N][N];
    int lower[N];
    int dp[1<<16];//注意这里数组要开够
    void floyd(int n) {
      int i,j,k;
      for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
        for(j=1;j<=n;j++) {
                if(ma[i][k]>=inf||ma[k][j]>=inf)continue;
        if(ma[i][j]>ma[i][k]+ma[k][j])
        ma[i][j]=ma[i][k]+ma[k][j];
        }
      return ;
    }
    int vis[N];
    int judge(int n) {
      int k=0,num=0;
      while(n) {
        vis[k]=n%2;
        if(vis[k])
            num++;
        n/=2;
        k++;
      }
      return num;
    }
    int main() {
          int n,m,i,j,k,sum,degree[N],f,to[N],low,last;
          lower[0]=1;
          for(i=1;i<=17;i++)//二进制
            lower[i]=lower[i-1]*2;
          while(scanf("%d",&n),n) {
            scanf("%d",&m);
            for(i=1;i<=n;i++)
                for(j=1;j<=n;j++)
                ma[i][j]=(i==j)?0:inf;
                memset(degree,0,sizeof(degree));
                sum=0;
            while(m--) {
                scanf("%d%d%d",&i,&j,&k);
                if(ma[i][j]>k)
                    ma[i][j]=ma[j][i]=k;
                degree[i]++;//求解度数
                degree[j]++;
                sum+=k;
            }
            f=0;
            for(i=1;i<=n;i++)
                if(degree[i]%2==1)
                    to[f++]=i;
                if(f==0) {//判断是否是欧拉回路
                    printf("%d
    ",sum);
                    continue;
                }
            floyd(n);//求解任意两点的距离实际用的只有奇度顶点
            // printf("z
    ");
            low=1<<f;
             for(i=1;i<low;i++)//初始化
                dp[i]=inf;
                dp[0]=0;
                //printf("%d
    ",f);
            for(i=1;i<low;i++) {
                memset(vis,0,sizeof(vis));
                if(judge(i)%2==1)continue;//如果是奇数不符合
                for(j=0;j<f;j++)
                for(k=0;k<f;k++) {
                        if(j==k)continue;//不能相同
                     if(!vis[j]||!vis[k])continue;//只要有一个没有就不符合
                     last=i^lower[j]^lower[k];
                     if(dp[i]>dp[last]+ma[to[j]][to[k]])//当前的状态=去掉j和k的状态+j和k的最短路
                     dp[i]=dp[last]+ma[to[j]][to[k]];
                }
            }
           // printf("%d
    ",dp[low-1]);
            printf("%d
    ",sum+dp[low-1]);//奇度顶点的总的最小匹配为重复边+总的边数
          }
    return 0;
    }
    

  • 相关阅读:
    我爱java系列之---【微服务间的认证—Feign拦截器】
    我爱java系列之---【设置权限的三种解决方案】
    581. Shortest Unsorted Continuous Subarray
    129. Sum Root to Leaf Numbers
    513. Find Bottom Left Tree Value
    515. Find Largest Value in Each Tree Row
    155. Min Stack max stack Maxpop O(1) 操作
    painting house
    Minimum Adjustment Cost
    k Sum
  • 原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410625.html
Copyright © 2011-2022 走看看