加油鸭

链接:https://ac.nowcoder.com/acm/contest/553/C
来源:牛客网

Chino的数学很差,因此Cocoa非常担心。今天,Cocoa准备教Chino和排队有关的问题。
我们总是会学各种排列组合的问题,那些题目大多数都是套路。而Cocoa不喜欢套路。
通常来说,每个人在排队的时候都会对前一个人有所意见,而如果他们排在第一个,也会颇有微词。因此,排一个尽可能让更多人满意的队伍是一件难事。
假设我们要给个人排队,,表示了排在之前一个给带来的舒适度,而就表示了排在第一位的舒适度。
通过一番模拟,Chino当然计算出了最优的方案,不过Cocoa希望Chino能计算地快一点。
题目对于Chino来说太难啦,你能帮一帮Chino吗?

第一次遇到状态压缩dp题目 最后照着答案敲还是敲错了 赶紧过来补一波题目

因为n只有18 那么一定要想到二进制表示所有的状态 想到状态压缩dp

如果只是dp[i] i的二进制下j位位1表示进队伍了 的话 那么显然是不够记录当前的所以状态 因为你不知道距离之间的排序顺序 那么我们就还需要一个状态来表示最后一位是谁了
那么就要dp[i][j] 来表示状态了

首先就是这个递推方程的边界了 边界就是只有一个人进队 且自己为队头
初始化
dp[i|(1<<j)][j] = w[j][j];
无后效性是针对于第一维i来说的 j的话有后效性 但是每次转移去最优
接下来就是递推方程了 保证没有后效性 dp[i-1] 不会影响到dp[i]的最优性 接下来就是转移了
如果在i的二进制下j没有进入队伍 那么就把j当做最后一个人进队 然后就是要保证j为最后一个的时候这个值要最大 那么就和排在j前面的是谁有关系了 因为这个题目值关心两两之间的排序
就有如下递推方程

dp[i|(1<<j)][j] = max(dp[i|(1<<j)][j],dp[i][k] + w[k][j]); 遍历一下到底是谁排在j前面就好了
这里的k是要在i的二进制内的!!
代码如下

#include<bits/stdc++.h>// dp三要素  状态转移方程 无后效性 边界条件 比较一下到底是谁站在最后一位

using namespace std;

const int maxn = 20;

int dp[maxn][maxn],a[maxn][maxn];  //dp[i][j]   i 表示有多少人进队了 j表示最后一位的是谁

int main()
{
    int n;
    cin>>n;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++) cin>>a[i][j];
    }
    for(int i = 0; i < (1<<n); i++) // 从进队伍来考虑 这样就不会出错 不会漏算东西了
    {
        for(int j = 0; j < n; j++)
        {
            if((i>>j&1)==0) // j没有进队伍 让j进队伍且排在最后面 无后效性
            {
                if(i==0) dp[i|(1<<j)][j]=max(dp[i|(1<<j)][j],a[j][j]); // 比较一下谁排在排头
                else
                {
                    for(int k = 0; k < n; k++)
                    {
                        if((i>>k&1)!=0) // 
                        {
                            dp[i|(1<<j)][j]=max(dp[i|(1<<j)][j],dp[i][k]+a[j][k]);
                        }
                    }
                }
            }
        }
    }
    int ans = 0;
    for(int i = 0; i < n; i++) ans = max(dp[(1<<n)-1][i],ans);
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值