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;
}
posted @ 2020-12-02 14:26  youth518  阅读(99)  评论(0编辑  收藏  举报