http://www.fjutacm.com/Contest.jsp?cid=705#P10
题
意
:
n
个
点
,
m
条
双
向
路
,
其
中
有
P
个
点
有
宝
石
。
你
要
从
第
1
个
点
出
发
,
收
集
完
所
有
的
宝
石
,
最
后
从
第
n
个
点
离
开
。
题意:n个点,m条双向路,其中有P个点有宝石。你要从第1个点出发,收集完所有的宝石,最后从第n个点离开。
题意:n个点,m条双向路,其中有P个点有宝石。你要从第1个点出发,收集完所有的宝石,最后从第n个点离开。
题解:
因
为
p
只
有
12
,
所
以
第
一
眼
状
压
因为p只有12,所以第一眼状压
因为p只有12,所以第一眼状压
d
p
[
i
]
[
j
]
表
示
在
j
点
,
当
前
取
了
宝
石
的
状
态
为
i
时
的
最
短
路
,
那
么
d
p
[
0
]
[
1
]
=
0
dp[i][j]表示在j点,当前取了宝石的状态为i时的最短路,那么dp[0][1] = 0
dp[i][j]表示在j点,当前取了宝石的状态为i时的最短路,那么dp[0][1]=0
(
对
于
状
态
i
,
如
果
其
二
进
制
某
位
为
1
,
所
以
该
位
的
宝
石
已
经
取
了
。
)
(对于状态i,如果其二进制某位为1,所以该位的宝石已经取了。)
(对于状态i,如果其二进制某位为1,所以该位的宝石已经取了。)
首
先
用
f
l
o
y
d
预
处
理
出
任
意
两
点
间
最
短
路
。
然
后
遍
历
状
态
s
z
和
点
n
和
宝
石
p
,
最
后
的
答
案
就
是
m
i
n
(
d
p
[
(
1
<
<
p
)
−
1
]
[
i
]
)
首先用floyd预处理出任意两点间最短路。然后遍历状态sz和点n和宝石p,最后的答案就是 min(dp[(1<<p)-1][i])
首先用floyd预处理出任意两点间最短路。然后遍历状态sz和点n和宝石p,最后的答案就是min(dp[(1<<p)−1][i])
转
移
方
程
:
转移方程:
转移方程:
d
p
[
i
dp[i
dp[i^
(
1
<
<
(
k
−
1
)
)
]
[
a
[
k
]
]
=
m
i
n
(
d
p
[
i
(1<<(k-1))][a[k]] = min(dp[i
(1<<(k−1))][a[k]]=min(dp[i^
(
1
<
<
(
k
−
1
)
)
]
[
a
[
k
]
]
,
d
[
j
]
[
a
[
k
]
]
+
d
p
[
i
]
[
j
]
)
,
i
是
枚
举
的
状
态
,
j
是
枚
举
的
点
,
k
是
第
几
个
宝
石
(1<<(k-1))][a[k]],d[j][a[k]] + dp[i][j]),i是枚举的状态,j是枚举的点,k是第几个宝石
(1<<(k−1))][a[k]],d[j][a[k]]+dp[i][j]),i是枚举的状态,j是枚举的点,k是第几个宝石
具体解释看代码吧。。
如有错误欢迎指出。 (手动狗头.jpg)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#include<vector>
using namespace std;
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define mem(a,b) memset(a,b,sizeof(a));
#define lowbit(x) x&-x;
#define debugint(name,x) printf("%s: %d\n",name,x);
#define debugstring(name,x) printf("%s: %s\n",name,x);
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-6;
const int maxn = 1e5+5;
const int mod = 1e9+7;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll dp[1<<15][205]; //dp[i][j]表示在j点,已经取了宝石的状态为i时的最短路,
ll d[205][205];
int n,m,p;
int a[205];
void floyd(){
for(int k = 0; k <= n; k++){
for(int i = 0; i <= n; i++){
for(int j = 0; j <= n; j++){
d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
}
}
}
}
int main(){
int t;
cin>>t;
while(t--){
scanf("%d%d%d",&n,&m,&p);
mem(d,inf);
mem(dp,inf);
for(int i = 1; i <= p; i++){
scanf("%d",&a[i]);
}
int u,v;
ll w;
for(int i = 1; i <= m; i++){
scanf("%d%d%lld",&u,&v,&w);
d[u][v] = d[v][u] = min(d[u][v],w);
}
floyd();
int sz = (1<<p);
dp[0][1] = 0;
ll ans = 1e18;
for(int i = 0; i < sz; i++){
for(int j = 1; j <= n; j++){
if(i == ((1<<p)-1)){ //此时i状态二进制位全为1,表示所有宝石都取了,更新答案
ans = min(ans, 1ll*dp[i][j]+d[j][n]);
}
for(int k = 1; k <= p; k++){ //枚举还没有取的宝石
if((i&(1<<(k-1))) == 0){
//i^(1<<(k-1))表示取了第k个宝石的状态,dp[i^(1<<(k-1))][a[k]]表示在点a[k]处取得第k个宝石的状态时的最短路
ll &nex = dp[i^(1<<(k-1))][a[k]];
//因为i是没有取第k个宝石的状态,所以dp[i][j]+d[j][a[k]]表示从第k个宝石以外的点处转移到a[k]的最短路
nex = min(nex, d[j][a[k]] + dp[i][j]);
}
}
}
}
if(ans == 1e18) puts("-1");
else printf("%lld\n",ans);
}
}
本文介绍了一个关于收集宝石的图论问题的解决方法,使用状压DP算法来寻找从起点出发,收集所有宝石并返回终点的最短路径。通过Floyd算法预处理最短路径,结合状态压缩技巧,实现对小规模宝石位置的有效遍历。
341

被折叠的 条评论
为什么被折叠?



