BZOJ4479 : [Jsoi2013]吃貨jyy

Claris發表於2017-09-01

若$k\leq 15$,那麼可以設$d[i][S]$表示經過了$S$集合的邊,現在位於$i$點的最短路。

可以用Dijkstra演算法在$O(n^22^k)$時間內求出。

否則若$k>15$,那麼最壞情況下,它們會形成一個團,將這$k$條邊連上後,圖中最多剩下$7$個連通塊。

如果知道哪些邊要走,哪些邊不走的話,那麼只要存在尤拉回路就可以。

也就是說,所有點的度數都是偶數,且從$1$出發可以到達$k$條邊的端點。

於是考慮DP,設$f[i][j][k]$表示考慮前$i$條邊,目前連通性為$j$,每個點度數的奇偶性為$k$的最小代價。

時間複雜度$O(n^2Bell(7)2^n)$,狀態比較稀疏,可以通過。

 

#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
typedef pair<int,int>P;
typedef pair<int,P>PI;
typedef long long ll;
const int N=13,M=880,inf=100000000,LIM=15;
int n,m,K,o,i,j,k,x,y,z;
inline void up(int&a,int b){a>b?(a=b):0;}
namespace SMALL{
int f[N][N][2],g[N][N],d[N][1<<LIM];priority_queue<PI,vector<PI>,greater<PI> >q;
inline void ext(int x,int y,int z){
  if(d[x][y]<=z)return;
  q.push(PI(d[x][y]=z,P(x,y)));
}
void solve(){
  for(i=0;i<n;i++)for(j=0;j<n;j++)f[i][j][0]=-1;
  for(i=0;i<n;i++)for(j=0;j<n;j++)g[i][j]=inf;
  for(i=0;i<K;i++){
    scanf("%d%d%d",&x,&y,&z);x--,y--;
    f[x][y][0]=f[y][x][0]=i;
    f[x][y][1]=f[y][x][1]=z;
  }
  scanf("%d",&m);
  while(m--){
    scanf("%d%d%d",&x,&y,&z);x--,y--;
    up(g[x][y],z),up(g[y][x],z);
  }
  for(i=0;i<n;i++)for(j=0;j<1<<K;j++)d[i][j]=inf;
  ext(0,0,0);
  while(!q.empty()){
    PI t=q.top();q.pop();
    x=t.second.first,y=t.second.second,z=t.first;
    if(z>d[x][y])continue;
    for(i=0;i<n;i++){
      if(~f[x][i][0])ext(i,y|(1<<f[x][i][0]),z+f[x][i][1]);
      ext(i,y,z+g[x][i]);
    }
  }
  printf("%d",d[0][(1<<K)-1]);
}
}
namespace BIG{
bool must[N];
int a[N],h,t,e[N][N],dp[2][1<<N],g[M][N][N],ans=inf;
int w[2][M][1<<N];
char T,v[2][M][1<<N];
short s[2][M][1<<N],cnt[2][M];
ll q[M];
map<ll,int>id;
inline void merge(int x,int y){
  x=a[x],y=a[y];
  for(int i=0;i<n;i++)if(a[i]==x)a[i]=y;
}
inline ll encode(){
  int i,m=0;ll t=0;
  static int v[N];
  for(i=0;i<n;i++)v[a[i]]=-1;
  for(i=0;i<n;i++){
    if(v[a[i]]<0)v[a[i]]=m++;
    t=t<<4|v[a[i]];
  }
  return t;
}
inline void decode(ll f){for(int i=n-1;~i;i--)a[i]=f&15,f>>=4;}
inline bool check(ll f){
  decode(f);
  for(int i=0;i<n;i++)if(must[i]&&a[i]!=a[0])return 0;
  return 1;
}
inline int ext(ll x){
  int&o=id[x];
  if(o)return o;
  q[o=++t]=x;
  return o;
}
inline void clr(){
  T++;
  for(int i=1;i<=t;i++)cnt[o^1][i]=0;
}
inline void add(int x,int y,int z){
  if(z>=inf)return;
  if(v[o^1][x][y]<T){
    v[o^1][x][y]=T;
    w[o^1][x][y]=z;
    s[o^1][x][cnt[o^1][x]++]=y;
    return;
  }
  up(w[o^1][x][y],z);
}
void solve(){
  for(i=0;i<n;i++)a[i]=i;
  for(i=1;i<1<<n;i++)dp[0][i]=inf;
  while(K--){
    scanf("%d%d%d",&x,&y,&z);x--,y--;
    must[x]=must[y]=1;
    merge(x,y);
    for(i=0;i<1<<n;i++)dp[o^1][i]=inf;
    for(i=0;i<1<<n;i++)if(dp[o][i]<inf){
      up(dp[o^1][i^(1<<x)^(1<<y)],dp[o][i]+z);
      up(dp[o^1][i],dp[o][i]+z+z);
    }
    o^=1;
  }
  decode(encode());
  h=1;
  ext(encode());
  while(h<=t){
    ll x=q[h];
    for(i=0;i<n;i++)for(j=0;j<n;j++){
      decode(x);
      merge(i,j);
      g[h][i][j]=ext(encode());
    }
    h++;
  }
  scanf("%d",&m);
  for(i=0;i<n;i++)for(j=0;j<n;j++)e[i][j]=inf;
  while(m--){
    scanf("%d%d%d",&x,&y,&z);x--,y--;
    if(x==y)continue;
    if(x>y)swap(x,y);
    up(e[x][y],z);
  }
  clr();
  for(i=0;i<1<<n;i++)add(1,i,dp[o][i]);
  o^=1;
  for(x=0;x<n;x++)for(y=0;y<n;y++)if(e[x][y]<inf){
    z=e[x][y];
    clr();
    for(i=1;i<=t;i++)for(j=0;j<cnt[o][i];j++){
      int S=s[o][i][j],f=w[o][i][S];
      add(i,S,f);
      add(g[i][x][y],S^(1<<x)^(1<<y),f+z);
      add(g[i][x][y],S,f+z+z);
    }
    o^=1;
  }
  for(i=1;i<=t;i++)if(check(q[i]))for(j=0;j<cnt[o][i];j++)if(!s[o][i][j])up(ans,w[o][i][0]);
  printf("%d",ans);
}
}
int main(){
  scanf("%d%d",&n,&K);
  if(!K)return puts("0"),0;
  if(K<=LIM)SMALL::solve();else BIG::solve();
  return 0;
}

  

相關文章