• 传球游戏之最小总代价


    题目

    Description

    n个人在做传递物品的游戏,编号为1-n。游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;求当物品经过所有n个人后,整个过程的总代价是多少。

    Input

    n个人在做传递物品的游戏,编号为1-n。游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位;下一个人可以传递给未接过物品的任意一人。即物品只能经过同一个人一次,而且每次传递过程都有一个代价;不同的人传给不同的人的代价值之间没有联系;求当物品经过所有n个人后,整个过程的总代价是多少。

    Output

    一个数,为最小的代价总和。

    Sample Input

    2
    -1 9794
    2724 –1

    Sample Output

    2724

    思路

    这是一道状压$dp$ 的入门题;

    首先我们可以设 $dp[i][k]$ 表示球到了第$i$ 个手里并且经过了的人编号的集合为 $k$;

    就是 $dp[3][1,5,3]$ 表示传到了第$3$ 个人手里并且经过了$1,5,3$ 这几个人;

    那么怎么去表示一个集合呢;

    用一个二进制数表示如果第$i$ 位二进制是$1$,代表这个集合包涵 $i$;

    具体基础的状压知识请看:

    状压dp基本知识

    那么状态转移方程很显然就是:

    $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
    ",ans);//输出answer 
    }
  • 相关阅读:
    [JLOI2011] 飞行路线
    高精度运算模板
    Dijkstra算法模板
    [SDOI2010] 外星千足虫
    [SDOI2006] 线性方程组
    [CTSC2014] 企鹅QQ
    模板三连击:树状数组+线段树+主席树
    [ZJOI2008] 树的统计
    [国家集训队] 礼物
    [洛谷P4720] [模板] 扩展卢卡斯
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13471170.html
Copyright © 2020-2023  润新知