首页 > 其他分享 >[POI2007] ATR-Tourist Attractions

[POI2007] ATR-Tourist Attractions

时间:2024-04-16 11:14:54浏览次数:23  
标签:状态 le int Attractions POI2007 状压 停留 ATR 城市

[POI2007] ATR-Tourist Attractions

题目背景

English Edition

题目描述

给出一张有 \(n\) 个点 \(m\) 条边的无向图,每条边有边权。

你需要找一条从 \(1\) 到 \(n\) 的最短路径,并且这条路径在满足给出的 \(g\) 个限制的情况下可以在所有编号从 \(2\) 到 \(k+1\) 的点上停留过。

每个限制条件形如 \(r_i, s_i\),表示停留在 \(s_i\) 之前必须先在 \(r_i\) 停留过。

注意,这里的停留不是指经过

输入格式

第一行三个整数 \(n,m,k\)。

之后 \(m\) 行,每行三个整数 \(p_i, q_i, l_i\),表示在 \(p_i\) 和 \(q_i\) 之间有一条权为 \(l_i\) 的边。

之后一行一个整数 \(g\)。

之后 \(g\) 行,每行两个整数 \(r_i, s_i\),表示一个限制条件。

输出格式

输出一行一个整数,表示最短路径的长度。

样例 #1

样例输入 #1
8 15 4
1 2 3
1 3 4
1 4 4
1 6 2
1 7 3
2 3 6
2 4 2
2 5 2
3 4 3
3 6 3
3 8 6
4 5 2
4 8 6
5 7 4
5 8 6
3
2 3
3 4
3 5
样例输出 #1
19

提示

对于 \(100\%\) 的数据, 满足:

  • \(2\le n\le2\times10^4\)
  • \(1\le m\le2\times10^5\)
  • \(0\le k\le\min(20, n-2)\)
  • \(1\le p_i<q_i\le n\)
  • \(1\le l_i\le 10^3\)
  • \(r_i, s_i \in [2,k+1], r_i\not=s_i\)
  • 保证不存在重边且一定有解。

解析

一道看起来是道最短路其实就是最短路的状压DP,\(k\) 的范围决定了状压的内容就是必须停留的城市,

首先处理 \(1--k+1\) 每一座城市到 \(n\) 的最短路, 方便之后状态转移,

然后处理约束条件, 既然最多只有 \(20\) 座城市, 我们就可以先记录在经过城市 \(k\) 之前必须停留的城市,

之后在遍历状态时这个就是转移的条件. \(dp[i][j]\) 表示状态为 \(i\) 并且当前停留在城市 \(j\).

状压的三层循环很清晰,第一层遍历所有状态,第二层遍历 \(2--k+1\) 个城市( \(i\) 状态时停留在 \(j\) ),

第三层遍历 \(2--k+1\) 个城市(把这个城市加入当前状态),此时状态变为 \(dp[i | (1<<(h-2))][h]\)。

#include<bits/stdc++.h>
using namespace std;
const int N = 20005;
int n,m,k,q;
int a[(1<<20)+5];
int dp[(1<<20)+5][21];
struct E
{
	int nxt,to,w;
} e[400005];
int head[N],tot;
void add(int u,int v,int w)
{
	e[++tot]={head[u],v,w};
	head[u]=tot;
}
int d[30][N];
bool v[N];
void dj(int s)
{
	memset(d[s],0x3f,sizeof(d[s]));
	memset(v,0,sizeof(v));
	priority_queue<pair<int,int> > q;
	d[s][s]=0; q.push(make_pair(0,s));
	while(!q.empty())
	{
		int u=q.top().second; q.pop();
		if(v[u]) continue;
		v[u]=1;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int to=e[i].to;
			if(!v[to]&&d[s][to]>d[s][u]+e[i].w)
			{
				d[s][to]=d[s][u]+e[i].w;
				q.push(make_pair(-d[s][to],to));
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}	
	for(int i=1;i<=k+1;i++) dj(i);
	if(k==0)
	{
		printf("%d",d[1][n]);
		return 0;
	}
	
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		a[y] |= (1<<(x-2));
	}
	memset(dp,0x3f,sizeof(dp));
	dp[0][1]=0;
	for(int i=2;i<=k+1;i++)
	{
		if(!a[i]) dp[(1<<(i-2))][i]=d[1][i];
	}
	for(int i=0;i<(1<<k);i++)
	{
		for(int j=2;j<=k+1;j++)
		{
			if(!(i & (1<<(j-2)))) continue;
			for(int h=2;h<=k+1;h++)
			{
				if(!(i & (1<<(h-2)))&&(i | a[h])==i)
				dp[i | (1<<(h-2))][h]=min(dp[i | (1<<(h-2))][h],dp[i][j]+d[j][h]);
			}
		}
	}
	int ans=1e9;
	for(int i=2;i<=k+1;i++) ans=min(ans,dp[(1<<k)-1][i]+d[i][n]);
	printf("%d\n",ans);
	return 0;
}

以上为非AC代码

优化

洛谷很不人道的把空间卡到了 \(64MB\) ,正解为滚动数组,但是不会~~

于是采用玄学做法,在上述代码中我们开了 \((1<<20)\) 的数组存 \(20\) 个城市的状态,

但其实我们判断约束条件时有一个城市是没用的,那就是当前这个城市本身,

所以实际上有用的只有 \((1<<19)\) , 有一位是永远空着的。

所以每次判断时我们把中间这位跳过去(如下图):

int solve(int x, int y)//x表示状态,y表示要跳过的这一位。
{
	return (y & ((1 << x - 1) - 1)) + (y >> x << (x - 1));
}

有点绕,简单来说就是把前 \(x-1\) 和 \(x+1\) 到最后一位保留,然后拼接在一起,

#include<bits/stdc++.h>
using namespace std;
const int N = 20001;
int n,m,k,p;
int a[(1<<20)];
int dp[(1<<19)][21];
struct E
{
	int nxt,to,w;
} e[400001];
int head[N],tot;
void add(int u,int v,int w)
{
	e[++tot]={head[u],v,w};
	head[u]=tot;
}
int d[25][N];
bool v[N];
priority_queue<pair<int,int> > q;
void dj(int s)
{
	memset(d[s],0x3f,sizeof(d[s]));
	memset(v,0,sizeof(v));
	
	d[s][s]=0; q.push(make_pair(0,s));
	while(!q.empty())
	{
		int u=q.top().second; q.pop();
		if(v[u]) continue;
		v[u]=1;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int to=e[i].to;
			if(!v[to]&&d[s][to]>d[s][u]+e[i].w)
			{
				d[s][to]=d[s][u]+e[i].w;
				q.push(make_pair(-d[s][to],to));
			}
		}
	}
}
int solve(int x, int y)
{
	return (y & ((1 << x - 1) - 1)) + (y >> x << (x - 1));
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}	
	for(int i=1;i<=k+1;i++) dj(i);
	if(k==0)
	{
		printf("%d",d[1][n]);
		return 0;
	}
	
	scanf("%d",&p);
	for(int i=1;i<=p;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		a[y] |= (1<<(x-2));
	}
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=k;i++)
	{
		if(!a[i+1]) dp[0][i]=d[1][i+1];
	}
	for(int i=1;i<(1<<k);i++)
	{
		for(int j=2;j<=k+1;j++)
		{
			if((i & (1<<j-2))&&((i & a[j])==a[j]))
			for(int h=2;h<=k+1;h++)
			{
				if(i & (1<<h-2))
				dp[solve(j-1,i)][j-1]=min(dp[solve(j-1,i)][j-1],dp[solve(h-1,(i-(1<<j-2)))][h-1]+d[h][j]);
			}
		}
	}
	int ans=1e9;
	for(int i=2;i<=k+1;i++) ans=min(ans,dp[(1<<k-1)-1][i-1]+d[i][n]);
	printf("%d\n",ans);
	return 0;
}

总结

根据数据范围找状压内容,对于约束条件比较熟悉的是差分约束的形式,

其实换个思路,约束条件也可以看成状态转移的条件,这样再看状压就合情合理了。

标签:状态,le,int,Attractions,POI2007,状压,停留,ATR,城市
From: https://www.cnblogs.com/ppllxx/p/18136668

相关文章

  • POI2007ATR-Tourist Attractions
    最短路#状压dp#滚动优化#POI#Year2007从前\(k\)个跑\(dijksta\),对这\(k\)个点到达的状态状压会MLE,考虑每次转移都只会增加一个状压下的\(1\),按照\(popcount\)分组做滚动//Author:xiaruizeconstintINF=0x3f3f3f3f;constintMOD=1000000007;constin......
  • POI2007TET-Tetris Attack
    POI#Year2007#贪心#树状数组考虑每一对数的最小代价为,将当前的换到最近的下面用树状数组记录中间有几个没有被消掉的//Author:xiaruizeconstintN=2e5+10;intn,m;intla[N];structBIT{ inttr[N]; voidadd(intx,intv) { while(x<=m) { t......
  • POI2007POW-The Flood
    POI#Year2007#并查集#贪心按高度从小到大按顺序考虑每个点,将同样高度的点按顺序全部合并完,然后再遍历这些同样大小的点,如果一个点为关键点且它的联通块中没有抽水机,那么这个位置联通块的最低位置放一个抽水机可以证明这个贪心是最优的//Author:xiaruizeconstintN=1e3......
  • POI2007ODW-Weights
    进制#背包dp#贪心注意到呈倍数的性质,考虑按照倍数转换进制,贪心的选择小的按顺序选择//Author:xiaruizeconstintINF=0x3f3f3f3f3f3f3f3f;constintMOD=1000000007;constintN=2e5+10;intn,m;inta[N],b[N];piis[N];intcnt[N];vector<int>vec;int......
  • CF1162B Double Matrix 题解
    传送门说句实话,如果不是先写了Showstopper这道题的话,我应该会在这里卡很久,因为做Showstopper我就卡了很久QwQ。思路太像了,实在是太像了,与Showstopper想比,仅仅就是换成二维数组,求最大值变为找递增矩阵,处理方法一模一样:将数组\(a\)和\(b\)中较小的值存在一个数组里,较......
  • [POI2007] [LUOGU P3451]旅游景点 Tourist Attractions
    本题解由于作者太菜在POI及LUOGU上会TLE,该题解主要讲思路,剩下的内存优化请各位大佬自行补充,欢迎评论区讨论本题解运行时间10406ms,空间194584KiB题目描述FGD想从成都去上海旅游。在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情。经过这些城......
  • 2x2 光学器件的 S-Matrix
    2x2光学器件的S-Matrix正文正文对于一个2x2光学器件,这里我们以一个DC举例,假设它的端口命名方式如下:它的传输矩阵S通常我们认为是2x2的。如下:S=[S11......
  • LeetCode 1439. Find the Kth Smallest Sum of a Matrix With Sorted Rows
    原题链接在这里:https://leetcode.com/problems/find-the-kth-smallest-sum-of-a-matrix-with-sorted-rows/description/题目:Youaregivenan mxn matrix mat thathasitsrowssortedinnon-decreasingorderandaninteger k.Youareallowedtochoose exactlyo......
  • 【论文精读】Detecting Out-of-Distribution Examples with Gram Matrices 使用Gram矩
    文章目录一、文章概览(一)Gram矩阵1、Gram(格朗姆)矩阵的定义2、Gram矩阵计算特征表示3、风格迁移中的Gram矩阵(二)ood检测(三)核心思路:扩展Gram矩阵以进行分布外检测(四)研究成果二、模型细节(一)符号定义(二)Gram矩阵和高阶Gram矩阵(三)预处理(四)计算分层偏差(五)测试图像的总偏差(......
  • Megatron-DeepSpeed-GPU-多机训练
    Megatron-DeepSpeed-cuda-多机训练1.从ngc拉取pytorch:24.03-py3镜像2.安装nvidia-docker、创建容器3.安装Megatron-DeepSpeed环境4.安装openmpi和ssh服务5.拷贝公钥6.安装pdsh7.升级protobuf8.准备数据集9.创建配置文件10.开始测试本文演示了Megatron-DeepSpeed-GPU-......