现在的位置: 首页 > 综合 > 正文

HUD 4284 Travel && POJ 3229 The Best Travel Design(状态压缩)

2019年02月23日 ⁄ 综合 ⁄ 共 3367字 ⁄ 字号 评论关闭

题目链接~~>

做题感悟:这题真郁闷,之前做过POJ上的一个题和这个类似,但是不知为啥一直不对,看了一下别人的另一种思想才A。

解题思路:Floyd + 状态压缩

                因为要参观的城市才16个,可以用状态压缩枚举每一种状态。

              状态转移方程:dp[ S |(1<<j) ][ j ] = max( dp[ S |(1<<j) ][ j ],dp[ S ][ i ] - d[ v ][ u ] - c[ j ] + e[ j ] ).即:已经知道     dp[ S ][ i ] 以 i 为中间点更新 j 点的  dp[ S |(1<<j) ][ j ]  。

注意:有重边,e[ i ] 和 c[ i ] 不能合并,必须要先买证才能得到工资,开始点有可能包含在要参观的点中 。

代码:

#include<stdio.h>
#include<iomanip>
#include<vector>
#include<queue>
#include<fstream>
#include<string.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define INT long long int
using namespace std ;
const int INF = 999999999 ;
const int MX = 100 + 10 ;
const int MY = 20 ;
int n,m,num,tot ;
int dp[1<<16][MY],V[MY],e[MY],c[MY],d[MX][MX] ;
void init(int n)
{
   for(int i=0 ;i<n ;i++)
     for(int j=0 ;j<n ;j++)
        i==j ? d[i][j]=0 : d[i][j]=INF ;
}
void Floyd() // 最短路
{
   for(int k=0 ;k<n ;k++)
    for(int i=0 ;i<n ;i++)
      for(int j=0 ;j<n ;j++)
         d[i][j]=min(d[i][k]+d[k][j],d[i][j]) ;
}
void DP_SC(int pos)
{
   if(tot-c[pos]>=0)// 到达 0 点工作所剩钱
            dp[1<<pos][pos]=tot-c[pos]+e[pos] ;
   dp[0][pos]=tot ; // 到达 0 点没工作所剩钱
   for(int S=0 ;S<(1<<num) ;S++)
    for(int i=0 ;i<num ;i++)
      if(dp[S][i]!=-1)// 以 i 为中间点去更新 j 点
      {
          for(int j=0 ;j<num ;j++)
            if(i!=j&&!(S&(1<<j)))
            {
               int u = V[j],v=V[i] ;
               if(dp[S][i]>=d[v][u]+c[j])
                  dp[S|(1<<j)][j]=max(dp[S|(1<<j)][j],dp[S][i]-d[v][u]-c[j]+e[j]) ;
            }
      }
    int S=(1<<num)-1 ;
    bool flag=false ;
    for(int i=0 ;i<num ;i++)
      if(dp[S][i]>=d[V[i]][0])
      {
          flag=true ;
          break ;
      }
    cout<<(flag ? "YES" : "NO")<<endl ;
}
int main()
{
    int Tx,u,v,w ;
    scanf("%d",&Tx) ;
    while(Tx--)
    {
        scanf("%d%d%d",&n,&m,&tot) ;
        init(n) ;
        for(int i=0 ;i<m ;i++)
        {
            scanf("%d%d%d",&u,&v,&w) ;
            u-- ; v-- ;
            d[u][v]=d[v][u]=min(d[u][v],w) ;
        }
        scanf("%d",&num) ;
        int pos=-1 ;
        for(int i=0 ;i<num ;i++)
        {
            scanf("%d%d%d",&V[i],&e[i],&c[i]) ;
            V[i]-- ;
            if(!V[i])  pos=i ;
        }
        memset(dp,-1,sizeof(dp)) ;
        if(pos==-1)
        {
            V[num]=0 ; e[num]=0 ; c[num]=0 ;
            pos=num++ ;
        }
        Floyd() ;
        DP_SC(pos) ;
    }
    return 0 ;
}

题目链接~~>

做题感悟:这题和上面那题差不多,只要理解意思知道思路就ok了。

解题思路:Floyd + 状态压缩

       (1)输入必须拜访的城市时要记录一下,弄成二进制的形式(这里记为 P)便于后面查看是否完成,先Floyd 一下,求出任意两点之间的距离。

        (2)再用状态压缩枚举每一种状态dp [ i ] [ j ] 代表在状态 i 下最终到达 j 节点时所花费的最少时间,最终答案取 i 与 P相与 如果等于 P 就记录 i 状态中所拜访的城市个数与最优解比较的最大值。

代码:

#include<stdio.h>
#include<iomanip>
#include<vector>
#include<queue>
#include<fstream>
#include<string.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define INT long long int
using namespace std ;
const double INF = 99999999 ;
const int MX = 200 + 10 ;
const int MY = 15 ;
int n,m,mt ;
double tot  ;
double c[MX],d[MX][MX],dp[1<<16][17] ;
void init() // 初始化
{
    for(int i=0 ;i<n ;i++)
     for(int j=0 ;j<n ;j++)
       i==j ? d[i][j]=0 : d[i][j]=INF ;
    for(int S=0 ;S<(1<<n) ;S++)
      for(int i=0 ;i<n ;i++)
        dp[S][i]=-1 ;
}
void Floyd() // 求任意两点的最短路径
{
    for(int k=0 ;k<n ;k++)
     for(int i=0 ;i<n ;i++)
       for(int j=0 ;j<n ;j++)
           d[i][j]=min(d[i][j],d[i][k]+d[k][j]) ;
}
int judge(int S) // 记录 S 中 1 的个数
{
    int sum=0 ;
    for(int i=0 ;i<n ;i++)
      if(S&(1<<i))
         sum++ ;
    return sum ;
}
void DP_SC() // 状态压缩枚举
{
    int ans=0 ;
    bool flag=false ;
    for(int i=0 ;i<n ;i++)
        if(d[0][i]!=INF)
            dp[1<<i][i]=d[0][i]+c[i] ;
    for(int S=0 ;S<(1<<n) ;S++)
      for(int i=0 ;i<n ;i++)
        if(dp[S][i]!=-1)
        {
           for(int j=0 ;j<n ;j++)
            if(i!=j&&!(S&(1<<j)))
            {
               if(dp[S|(1<<j)][j]==-1)
                      dp[S|(1<<j)][j]=dp[S][i]+d[i][j]+c[j] ;
               else   dp[S|(1<<j)][j]=min(dp[S|(1<<j)][j],dp[S][i]+d[i][j]+c[j]) ;
            }
            if(((S&mt)==mt)&&dp[S][i]+d[i][0]<=tot)
            {
              flag=true ;
              ans = max(ans,judge(S)) ;
            }
        }
    if(flag)   cout<<ans<<endl ;
    else       cout<<"No Solution"<<endl ;
}
int main()
{
    double len ;
    int u,v,x,kind ;
    while(scanf("%d%d%lf",&n,&m,&tot),(n+m+tot))
    {
        init() ;
        tot=tot*12.0 ;
        mt=0 ;
        for(int i=0 ;i<m ;i++)
        {
            scanf("%d",&x) ;
            x-- ;
            mt=mt|(1<<x) ; // 以二进制的形式记录必须拜访的城市
        }
        for(int i=0 ;i<n ;i++)
            scanf("%lf",&c[i]) ;
        while(scanf("%d%d%lf%d",&u,&v,&len,&kind)&&(u+v+len+kind))
        {
            u-- ; v-- ;
            double ct = kind ? len/120.0 : len/80.0 ;
            d[u][v]=d[v][u]=min(d[u][v],ct) ; // 可能重边
        }
        Floyd() ;
        DP_SC() ;
    }
    return 0 ;
}

抱歉!评论已关闭.