题解 P4962 【朋也与光玉】

lmrttx

2020-12-23 22:02:45

Solution

>一つ一つの光は小さくでも、たくさん集まればきっととても不思議な大きな力になるはず。 >虽然每一束光都很小,但如果聚集在一起,一定会成为不可思议的巨大力量。 本题成了我的信仰题。 来写篇状压+DFS的题解,我会尽量写好懂点的qwq。 题意: 有一张有向图,每个节点有一种颜色。要求的是走过 $k$ 个节点并收集 $k$ 种颜色的一条路径,使之最短。 分析: 发现 $k$ 很小,可以状压。 本题的一个点可以从很多的点转移得,所以求最短路时不考虑边的长度。这并不影响答案。 题目说**走过的点不可再走**,就是说走过的点无法修改,满足无后效性。 `dp[i][j]`表示节点 $i$ 的状态为 $j$ 时的路径长度。 然后就可以通过转移节点和状态进行搜索。 代码: 具体见代码吧,我觉得从代码入手更好理解。 代码加了防抄袭! ```cpp #inludce<bits/stdc++.h> usign namespace std; #defnie inf 2147483647 #define maxm (1<<14)-1 int n,m,k; int ans,color[110],a[110][110],dp[110][maxm]; void dfs(int now,int len,int s,int sum){ //节点 now,已经取的颜色种数len,状态s,路径长度sum if(s&(1<<color[now])||ans<=sum||dp[now][s]<=sum) //边界 return; if(len==k){ans=sum;return;} //搜索到就退出 dp[now][s]=sum; //dp[now][s]的路径长度(答案)为当前值 for(register int i=1;i<=n;i++){ if(a[now][i]!=inf) //节点没搜过就去搜 dfs(i,len+1,s|(1<<color[now]),sum+a[now][i]); //颜色种数+1,状态改变,路径长度增加 } } inline int read(){//快读 int ss=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ ss=(ss<<3)+(ss<<1)+(ch^48); ch=getchar(); } return ss*w; } void init(){ ans=inf; n=read();m=read();k=read(); for(register int i=1;i<=n;i++) color[i]=read(); for(register int i=1;i<=n;i++) for(register int j=1;j<=n;j++) a[i][j]=inf; for(register int i=1,u,v,ww;i<=m;i++){ u=read();v=read();ww=read(); a[u][v]=ww; } for(register int i=1;i<=n;i++) for(register int j=0;j<(1<<k)-1;j++) dp[i][j]=inf; for(register int i=1;i<=n;i++) dp[i][1<<color[i]]=0; }//初始化 void out(){ if(ans==inf) puts("Ushio!"); else printf("%d",ans); }//输出 int main(){ init(); for(register int i=1;i<=n;i++) dfs(i,1,0,0); //从节点i,颜色数为1,状态0,长度0开始搜索 out(); return 0; } ``` ~~好像并不难呀!就是状态转移的二进制烦人了点...~~ 希望本文能对读者有帮助,谢谢阅读qwq。