BZOJ 1097 [POI2007]旅游景点atr

题意:FGD想从成都去上海旅游。在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情。经过这些城市的顺序不是完全随意的,比如说FGD不希望在刚吃过一顿大餐之后立刻去下一个城市登山,而是希望去另外什么地方喝下午茶。幸运的是,FGD的旅程不是既定的,他可以在某些旅行方案之间进行选择。由于FGD非常讨厌乘车的颠簸,他希望在满足他的要求的情况下,旅行的距离尽量短,这样他就有足够的精力来欣赏风景或者是泡MM了^_^. 整个城市交通网络包含N个城市以及城市与城市之间的双向道路M条。城市自1至N依次编号,道路亦然。没有从某个城市直接到它自己的道路,两个城市之间最多只有一条道路直接相连,但可以有多条连接两个城市的路径。任意两条道路如果相遇,则相遇点也必然是这N个城市之一,在中途,由于修建了立交桥和下穿隧道,道路是不会相交的。每条道路都有一个固定长度。在中途,FGD想要经过K(K<=N-2)个城市。成都编号为1,上海编号为N,而FGD想要经过的N个城市编号依次为2,3,…,K+1. 举例来说,假设交通网络如下图。FGD想要经过城市2,3,4,5,并且在2停留的时候在3之前,而在4,5停留的时候在3之后。那么最短的旅行方案是1-2-4-3-4-5-8,总长度为19。注意FGD为了从城市2到城市4可以路过城市3,但不在城市3停留。这样就不违反FGD的要求了。并且由于FGD想要走最短的路径,因此这个方案正是FGD需要的。

我们注意到K非常小,所以需要经过的城市可以状压起来,预处理出每对必须经过的城市之间的最短路,之后由小的状态向大的状态转移就好了,这题常数卡得比较紧,最短路尽量写heap+dijkstra,并且DP时判掉冗余状态,就能过了

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int maxn=20000+10;
const int maxm=1<<21;
int d[maxn],dis[23][maxm],n,m,k,f[23][23],pre[maxn],id[maxn],vis[maxn];
bool inq[23][maxm];
struct node
{
  int to,cost;
};
struct arr
{
  int p,s;
};
struct vv
{
  int p,v;
  bool operator <(const vv g)const
  {
    return v>g.v;
  }
};
priority_queue<vv> Q;
vector<node> g[maxn];
void dijkstra(int p)
{
  memset(vis,0,sizeof(vis));
  memset(d,127/2,sizeof(d));
  d[id[p]]=0;Q.push((vv){id[p],d[id[p]]});
  while(!Q.empty())
  {
    vv x=Q.top();Q.pop();if(vis[x.p]) continue;
    vis[x.p]=1;
    for(int i=0;i<g[x.p].size();i++)
    {
      node e=g[x.p][i];int v=e.to;
      if(d[v]>d[x.p]+e.cost)
      {
        d[v]=d[x.p]+e.cost;
        Q.push((vv){v,d[v]});
      }
    }
  }
  for(int i=1;i<=k+1;i++)
    f[p][i]=d[i];
  f[p][k+2]=d[n];
}
void spfa(int p)
{
  memset(d,127/2,sizeof(d));
  d[id[p]]=0;queue<int> Q;Q.push(id[p]);inq[p][id[p]]=1;
  while(!Q.empty())
  {
    int x=Q.front();Q.pop();inq[p][x]=0;
    for(int i=0;i<g[x].size();i++)
    {
      node e=g[x][i];int v=e.to;
      if(d[v]>d[x]+e.cost)
      {
        d[v]=d[x]+e.cost;
        if(!inq[p][v])
        {
          inq[p][v]=1;
          Q.push(v);
        }
      }
    }
  }
  for(int i=1;i<=k+1;i++)
    f[p][i]=d[i];
  f[p][k+2]=d[n];
}
void dp()
{
  memset(inq,0,sizeof(inq));queue<arr> Q;
  memset(dis,127/2,sizeof(dis));dis[1][0]=0;
  for(int s=0;s<(1<<k);s++)
    for(int x=1;x<=k+2;x++)
      for(int i=1;i<=k+2;i++)
        if((pre[id[i]]&s)==pre[id[i]]&&i!=x)
        {   
          int ns=s;
          if(i>1&&i<=k+1) ns=s|(1<<(i-2));
          if(dis[i][ns]>dis[x][s]+f[x][i])
            dis[i][ns]=dis[x][s]+f[x][i];
        }
}  
int main()
{
  //freopen("1097.in","r",stdin);
  //freopen("1097.out","w",stdout);
  scanf("%d%d%d",&n,&m,&k);
  for(int i=1;i<=m;i++)
  {
    int u,v,c;scanf("%d%d%d",&u,&v,&c);
    g[u].push_back((node){v,c});
    g[v].push_back((node){u,c});
  }
  for(int i=1;i<=k+1;i++) id[i]=i;
  id[k+2]=n;
  for(int i=1;i<=k+2;i++)
    dijkstra(i);
  int l;scanf("%d",&l);
  for(int i=1;i<=l;i++)
  {
    int x,y;scanf("%d%d",&x,&y);
    if(x<n) pre[y]+=1<<(x-2);
    else pre[y]+=1<<(k+1);
  }
  dp();
  printf("%d\n",dis[k+2][(1<<k)-1]);
  return 0;
}  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值