题目
Description
n个人在做传递物品的游戏,编号为1-n。游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;求当物品经过所有n个人后,整个过程的总代价是多少。
Input
n个人在做传递物品的游戏,编号为1-n。游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;求当物品经过所有n个人后,整个过程的总代价是多少。
Output
一个数,为最小的代价总和。
Sample Input1
2 -1 9794 2724 –1
Sample Output1
2724
思路
这是一道状压$dp$ 的入门题;
首先我们可以设 $dp[i][k]$ 表示球到了第$i$ 个手里并且经过了的人编号的集合为 $k$;
就是 $dp[3][1,5,3]$ 表示传到了第$3$ 个人手里并且经过了$1,5,3$ 这几个人;
那么怎么去表示一个集合呢;
用一个二进制数表示如果第$i$ 位二进制是$1$,代表这个集合包涵 $i$;
具体基础的状压知识请看:
那么状态转移方程很显然就是:
$dp[i][k]=min(dp[i][k],dp[j][k^(1<<(i-1))]+a[j][i]);$
$k$ 是枚举的一个集合,$i$ 和 $j$ 都是枚举的第几个人;
代码
#include<bits/stdc++.h> #define re register typedef long long ll; using namespace std; inline ll read() { ll a=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*f; } ll n; ll a[20][20]; ll dp[20][1<<16]; int main() { memset(dp,127/3,sizeof(dp));//初始化数组 n=read(); for(re ll i=1;i<=n;i++) for(re ll j=1;j<=n;j++) a[i][j]=read();//读入 for(re ll i=1;i<=n;i++) dp[i][1<<(i-1)]=0;//因为可以从任意一个人开始 for(re ll k=1;k<=(1<<n)-1;k++)//枚举每个集合 for(re ll i=1;i<=n;i++) { if(!k&(1<<(i-1)))//如果i 不在这个集合,那么进入下个循环 continue; for(re ll j=1;j<=n;j++) { if(i==j)//每个人不能从自己手里接过球 continue; if(k&(1<<(j-1)))//必须集合包涵 j ,也就是球传到 j,才能从 j 转移过来 dp[i][k]=min(dp[i][k],dp[j][k^(1<<(i-1))]+a[j][i]);//状态转移方程 }//修改代码艰难痕迹 // cout<<i<<" "<<k<<endl; // cout<<dp[i][k]<<endl; } ll ans=1<<30; for(re ll i=1;i<=n;i++) ans=min(ans,dp[i][(1<<n)-1]);//每个人统计一次,并且可以从任意人手里结束 printf("%lld\n",ans);//输出answer }
标签:传球,游戏,任意,ll,传递,物品,代价,dp 来源: https://www.cnblogs.com/wzx-RS-STHN/p/13471170.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。