2016"百度之星" - 初赛(Astar Round2A)Sitting in Line(★)

度度熊是他同时代中最伟大的数学家,一切数字都要听命于他。现在,又到了度度熊和他的数字仆人们玩排排坐游戏的时候了。游戏的规则十分简单,参与游戏的N个整数将会做成一排,他们将通过不断交换自己的位置,最终达到所有相邻两数乘积的和最大的目的,参与游戏的数字有整数也有负数。度度熊为了在他的数字仆人面前展现他的权威,他规定某些数字只能在坐固定的位置上,没有被度度熊限制的数字则可以自由地交换位置。
 

Input

第一行一个整数$T$,表示$T$组数据。 
每组测试数据将以如下格式从标准输入读入: 

$N$ 

$a_1 p_1$ 

$a_2 p_2$ 



$a_N P_N$ 

第一行,整数 $N (1 \leq N \leq 16)$,代表参与游戏的整数的个数。 

从第二行到第 $(N + 1)$ 行,每行两个整数,$a_{i} (-10000 \leq a_{i} \leq 10000)$、$p_{i} (p_{i} = -1$ 或 $0 \leq p_{i} < N)$,以空格分割。$a_{i}$代表参与游戏的数字的值,$p_{i}$代表度度熊为该数字指定的位置,如果$p_{i} = -1$,代表该数字的位置不被限制。度度熊保证不会为两个数字指定相同的位置。
 

Output

第一行输出:"Case #i:"。$i$代表第$i$组测试数据。 

第二行输出数字重新排列后最大的所有相邻两数乘积的和,即$max\{a_1\cdot a_2+a_2\cdot a_3+......+a_{N-1}\cdot a_N\}$。
 

Sample Input

    
    
2 6 -1 0 2 1 -3 2 4 3 -5 4 6 5 5 40 -1 50 -1 30 -1 20 -1

10 -1


题解:

  我们设定dp[1<<16][16]:dp[i][j]:i为当前选取的状态并以第j个数结尾的最大值,那么答案就是 max{dp[全集][k]} k属于0到n

  对于dp[i][j] , i这个状态已经填了x个数,我们准备填第x+1个数时, 如果当前位置必填某个数,那么 就只更新以规定的这个数结尾转移方程

  如果没有那就 枚举那么可以任意放的数来更新相应的状态及答案

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <queue>
#include <map>
#include <stack>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF  0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-10
#define maxn 2000010
#define MOD 1000000007

int n,m;
int pos[22],a[22],num[22];
long long dp[122000][18];

int main()
{
    int T,C = 1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int k = 0;
        memset(pos,INF,sizeof(pos));
        memset(dp,-INF,sizeof(dp));
        memset(num,0,sizeof(num));
        for(int i = 0; i < n; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            a[i] = x;
            if(y != -1)
            {
                pos[y] = i;
                num[i]++;
            }
        }

        if(pos[0] != INF)
            dp[(1<<pos[0])][pos[0]] = 0;
        else
        {
            for(int i = 0; i < n; i++)
                if(!num[i])
                    dp[1<<i][i] = 0;
        }
        for(int i = 1; i < (1<<n); i++)
        {
            //printf("%d\n",i);
            int cnt = 0;
            for(int j = 0; j < n; j++)
                if(i & (1<<j))
                    cnt++;
            if(pos[cnt] != INF)
            {
                cnt = pos[cnt];
                for(int j = 0; j < n; j++)
                    if(i & (1<<j) && j != cnt)
                        dp[i|(1<<cnt)][cnt] = max(dp[i|(1<<cnt)][cnt],dp[i][j]+a[cnt]*a[j]);
            }
            else
            {
                for(int j = 0; j < n; j++)
                    if(i & (1<<j))
                        for(int k = 0; k < n; k++)
                            if(!(i & (1<<k)))
                                dp[i|(1<<k)][k] = max(dp[i|(1<<k)][k],dp[i][j]+a[j]*a[k]);
            }
        }
        long long ans = -INF;
        printf("Case #%d:\n",C++);
        for(int i = 0; i < n; i++)
            ans = max(dp[(1<<n)-1][i],ans);
        printf("%I64d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值