BZOJ 1097 [POI2007]旅游景点atr

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=1097

思路

kk很小,考虑状压。

预处理出从11(k+1)(k+1)出发,到其他点的距离,令fsta,ifsta,i表示走过的点状态为stasta,最后一个到达的点为ii,所需要行走的最短举例,操作起来还是有很多要注意的地方的,具体见代码。

代码

#include <cstdio>
#include <queue>
#include <cstring>

const int maxn=20000;
const int maxm=400000;
const int maxk=21;
const int inf=0x3f3f3f3f;

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

int n,m,k,pre[maxm+10],now[maxn+10],son[maxm+10],tot,val[maxm+10];
int dist[maxk+2][maxn+10],f[(1<<maxk)+10][maxk+2],vis[maxn+10],t;
int from[maxk+2];
std::queue<int> q;

inline int ins(int a,int b,int c)
{
  pre[++tot]=now[a];
  now[a]=tot;
  son[tot]=b;
  val[tot]=c;
  return 0;
}

inline int spfa(int s)
{
  int* d=dist[s];
  d[s]=0;
  q.push(s);
  vis[s]=1;
  while(!q.empty())
    {
      int u=q.front(),j=now[u];
      q.pop();
      while(j)
        {
          int v=son[j];
          if(d[v]>d[u]+val[j])
            {
              d[v]=d[u]+val[j];
              if(!vis[v])
                {
                  vis[v]=1;
                  q.push(v);
                }
            }
          j=pre[j];
        }
      vis[u]=0;
    }
  return 0;
}

int main()
{
  n=read();
  m=read();
  k=read();
  while(m--)
    {
      int a=read(),b=read(),c=read();
      ins(a,b,c);
      ins(b,a,c);
    }
  t=read();
  for(int i=1; i<=t; ++i)
    {
      int a=read(),b=read();
      from[b]|=1<<(a-1);
    }
  for(int i=2; i<=k+1; ++i)
    {
      from[i]|=1;
    }
  memset(dist,63,sizeof dist);
  memset(f,63,sizeof f);
  for(int i=1; i<=k+1; ++i)
    {
      spfa(i);
      f[1<<(i-1)][i]=0;
    }
  for(int sta=1; sta<1<<(k+1); ++sta)
    {
      int flag=0;
      for(int i=2; i<=k+1; ++i)
        {
          if((((1<<(i-1))&sta)==(1<<(i-1)))&&((sta^from[i])!=(sta-from[i])))
            {
              flag=1;
              break;
            }
        }
      if(flag)
        {
          continue;
        }
      for(int i=1; i<=k+1; ++i)
        {
          if(sta&(1<<(i-1)))
            {
              int s=sta^(1<<(i-1));
              flag=0;
              for(int j=2; j<=k+1; ++j)
                {
                  if((((1<<(j-1))&s)==(1<<(j-1)))&&((s^from[j])!=(s-from[j])))
                    {
                      flag=1;
                      break;
                    }
                }
              if(flag)
                {
                  continue;
                }
              for(int j=1; j<=k+1; ++j)
                {
                  if(s&(1<<(j-1)))
                    {
                      f[sta][i]=std::min(f[sta][i],f[s][j]+dist[j][i]);
                    }
                }
            }
        }
    }
  int ans=inf;
  for(int i=1; i<=k+1; ++i)
    {
      ans=std::min(ans,f[(1<<(k+1))-1][i]+dist[i][n]);
    }
  printf("%d\n",ans);
  return 0;
}

转载于:https://www.cnblogs.com/Canopus-wym/p/10376187.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值