传递游戏【题解】
Description
毛大神最近在玩一个传递游戏,即有
游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。
即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;
毛大神想知道当物品经过所有
Format#
Input#
第一行为
以下为 N × N 的矩阵,第
Output#
输出共一行一个数,为最小的代价总和。
Samples#
输入数据 1
2
-1 9794
2724 -1
输出数据 1
2724
Limitation
对于 50% 的数据,
对于 100% 的数据,
浅浅的谈一下
-
第一反应肯定是搜索,然后回溯,时间复杂度乍一看可以,我们算算
-
……其实我也不会算(
一个蒟蒻),为什么不行呢,因为有人试过了, 分 -
怎么办?
-
一看这题目的问法, 就有一种
的思路,配合这数据范围,配合这题目,状压 应运而生! -
什么是状压
(其实我写过了,就copy过来了) -
“状压DP 又叫集合动态规划。是以结合信息为状态的特殊的动态规划的问题。主要有传统集合动态规划和基于连通性状态压缩的动态规划两种。” ————百度
-
是不是感觉很高大尚?(
我也觉得) -
其实很简单(
不是我说的) -
状压dp的样子
- 我们回到题目:我们把每个人看成每一个点,被传递过标记成一,他就变成了这样一个样子,很想二进制枚举是吧
-
怎么转移?#
-
(
):左移- 在十进制上是乘法
- 在二进制上是把整体往左挪一位,例如:
- (
): 右移,和左移原理一样,把整体往右移一位 - 对于每个状态,我们枚举这个点有没有选到:如
这个状态没有选第三个点
-
这就要用到左移 和 与运算了
-
如果我想要表示一个
,表示第三个状态已被选怎么办? -
很容易发现
-
于是我们可以总结:
-
于是我们就可以用
( 表示当前状态, 表示当前枚举到的点)来表示 状态有没有选点 -
考虑转移?#
-
枚举每个状态,再枚举这个状态选了那些点,再由这些点进行转移
-
什么意思?
-
例如状态
,这个状态包含 这 个点 -
如果点
可以接到点 后面 -
那么
-
然后输出
全部点被选即可 -
这道题咋写
-
假如枚举到一个状态
,枚举这个状态有多少个存在的点,再由这个点去转移到另外没有被选过的点 -
等等,怎么判断
中,球传到哪里了? -
于是,我们就要开二维数组
-
于是就有我们的满分代码了(注意
的优先级!)
#include<bits/stdc++.h>
//值得写解题报告!
using namespace std;
int dp[20][1<<20],n,g[20][20];
//dp[i][T]到了第i个人,状态为T
int main(){
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>g[i][j];
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=n;i++)dp[i][1<<(i-1)]=0;//初始
for(int t=1;t<1<<n;t++)//枚举状态
for(int i=1;i<=n;i++)
if(t&(1<<i-1))//在这里面
for(int j=1;j<=n;j++)
if(i!=j&&!(t&(1<<j-1)))//不在这里面
dp[j][t|(1<<j-1)]=min(dp[j][t|(1<<j-1)],dp[i][t]+g[i][j]);
int ans=1e9;
for(int i=1;i<=n;i++)
ans=min(ans,dp[i][(1<<n)-1]);
cout<<ans<<endl;
return 0;//注意"<<"优先级比"-"小
}
题外话:普及组的题竟然会考状压,没想到
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现