状压DP新姿势get√
需要注意的是,这题Main上原题的内存限制只有64MB。
首先以2到k+1为起点进行k次dijkstra求出:
1.dis[i][j]:i到j的最短路
2.d1[i]:i到1的最短路
3.dn[i]:i到n的最短路
用二进制状态a[i]表示走到i之前必须经过的点的集合。
设f[z][S][i]表示现在走过的集合中有z个元素,现在走过的集合为S,最后走过的点是i的最短路,
则由f[z][S][i]可以向f[z+1][S|(1<<j)][j]扩展,扩展的条件是j不属于S,且S包含a[j]。
边界条件:f[1][1<<i][i]=a[i]==0?d1[i]:inf
最后ans=min(f[k][(1<<k)-1][i]+dn[i])
z这一维显然可以滚动,S这一维最多只有$C_k^{\lceil\frac{k}{2}\rceil}$种,所以先预处理出:
1.id[i]:i这个数在与它1的个数相同的数字中排第几
2.cnt[i]:有i个1的数字个数
3.st[i][j]:有i个1的数字中排第j的是哪个数
时间复杂度$O(kn\log n+n^2C_k^{\lceil\frac{k}{2}\rceil})$。
空间复杂度$O(nC_k^{\lceil\frac{k}{2}\rceil})$。
#include<cstdio> const int N=20010,M=400010,K=20,Q=184756; int n,m,k,i,j,S,E,U,x,y,z,d[N],dis[K][K],d1[K],dn[K],a[K],g[N],v[M],w[M],nxt[M],ed; int id[1<<K],cnt[K],st[K][Q],f[2][Q][K],ans=1000000000; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline void up(int&a,int b){if(a>b)a=b;} inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} struct PI{ int x,y; PI(){} PI(int _x,int _y){x=_x,y=_y;} inline PI operator+(PI b){return x<b.x?PI(x,y):b;} }val[65537]; void build(int x,int a,int b){ val[x]=PI(ans,a); if(a==b)return; int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); } inline void change(int x,int a,int b,int c,int d){ if(a==b){val[x].x=d;return;} int mid=(a+b)>>1; c<=mid?change(x<<1,a,mid,c,d):change(x<<1|1,mid+1,b,c,d); val[x]=val[x<<1]+val[x<<1|1]; } void dijkstra(int S){ int i; for(i=1;i<=n;i++)d[i]=ans; build(1,1,n),change(1,1,n,S,d[S]=0); while(val[1].x<ans)for(change(1,1,n,x=val[1].y,ans),i=g[x];i;i=nxt[i])if(d[x]+w[i]<d[v[i]])change(1,1,n,v[i],d[v[i]]=d[x]+w[i]); } int main(){ read(n),read(m),read(k); while(m--)read(x),read(y),read(z),add(x,y,z),add(y,x,z); if(!k)return dijkstra(1),printf("%d",d[n]),0; for(read(m);m--;a[y-2]|=1<<(x-2))read(x),read(y); for(i=2;i<=k+1;i++){ for(dijkstra(i),j=2;j<=k+1;j++)dis[i-2][j-2]=d[j]; d1[i-2]=d[1],dn[i-2]=d[n]; } for(E=(1<<k)-1,S=1;S<=E;S++)id[S]=cnt[i=__builtin_popcount(S)-1],st[i][cnt[i]++]=S; for(x=0;x<cnt[0];x++)for(i=0;i<k;i++)f[0][x][i]=ans; for(i=0;i<k;i++)if(!a[i])f[0][id[1<<i]][i]=d1[i]; for(z=y=0;z<k-1;z++,y^=1){ for(x=0;x<cnt[z+1];x++)for(i=0;i<k;i++)f[y^1][x][i]=ans; for(x=0;x<cnt[z];x++)for(S=st[z][x],i=0;i<k;i++)if(f[y][x][i]<ans)for(U=E^S;U;U-=U&-U){ j=__builtin_ctz(U&-U); if((S&a[j])==a[j])up(f[y^1][id[S|(1<<j)]][j],f[y][x][i]+dis[i][j]); } } for(i=0;i<k;i++)up(ans,f[y][0][i]+dn[i]); return printf("%d",ans),0; }