P3451 [POI2007]ATR-Tourist ions 状压DPAttract
题意
分析:
吐槽一句,洛谷题面有锅
- 暴力
先跑 \(k+1\) 遍 \(dijkstra\) 预处理出前 \(k+1\) 个点之间的最短路,以及到 \(1\) 和 \(n\) 的最短路,然后 \(O(k^22^k)\) 的状压 DP 转移,但是由于 DP 数组的空间复杂度是 \(O(k\times2^k)\) 的,而题目的空限是 \(64M\)
- 正解
枚举的时候只从当前状态有的点向当前状态没有的点转移,由于 DP 方程一维已经枚举了一个在状态里的点,然后我们的第二维 \(O(2^k)\) 的状态可以减少一半,即 \(O(2^{k-1})\) 然后 \(80M\) 的内存优化到了 \(40M\)
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
using namespace std;
namespace zzc
{
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 2e4+5;
const int maxm = 2e5+5;
int head[maxn],dis[22][maxn],zt[25],f[22][1<<19];
int n,cnt=0,m,k,now,lst,g,ans=0x3f3f3f3f;
bool vis[maxn];
priority_queue<pii> q;
struct edge
{
int to,nxt,val;
}e[maxm<<1];
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].val=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void dijkstra(int st)
{
memset(dis[st],0x3f,sizeof(dis[st]));
memset(vis,false,sizeof(vis));
dis[st][st]=0;
q.push(mk(0,st));
while(!q.empty())
{
int u=q.top().sec;q.pop();
if(vis[u]) continue;
vis[u]=true;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(dis[st][v]>dis[st][u]+e[i].val)
{
dis[st][v]=dis[st][u]+e[i].val;
q.push(mk(-dis[st][v],v));
}
}
}
}
int del(int pos,int sta){return (sta&((1<<(pos-1))-1))+((sta>>pos)<<(pos-1));}
void work()
{
int a,b,c;
n=read();m=read();k=read();
for(int i=1;i<=m;i++)
{
a=read();b=read();c=read();
add(a,b,c);add(b,a,c);
}
for(int i=1;i<=k+1;i++) dijkstra(i);
if(k==0)
{
printf("%d\n",dis[1][n]);
return ;
}
g=read();
memset(f,0x3f,sizeof(f));
for(int i=1;i<=g;i++) a=read(),b=read(),zt[b]|=(1<<(a-2));
for(int i=2;i<=k+1;i++) if(!zt[i]) f[i-1][0]=dis[1][i];
for(int i=1;i<(1<<k);++i)
for(int to=1;to<=k;++to) if(i&(1<<(to-1))&&(i&zt[to+1])==zt[to+1])
for(int frm=1;frm<=k;++frm) if(to!=frm&&i&(1<<(to-1)))
f[to][del(to,i)]=min(f[to][del(to,i)],f[frm][del(frm,i-(1<<(to-1)))]+dis[frm+1][to+1]);
for(int i=2;i<=k+1;i++) ans=min(ans,f[i-1][(1<<(k-1))-1]+dis[i][n]);
printf("%d\n",ans);
}
}
int main()
{
zzc::work();
return 0;
}