Sitting in Line(hdu 5691)

Sitting in Line

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1322    Accepted Submission(s): 627


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

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

N

a1p1

a2p2

:

aNPN

第一行,整数 N(1N16) ,代表参与游戏的整数的个数。

从第二行到第 (N+1) 行,每行两个整数, ai(10000ai10000) pi(pi=1 0pi<N) ,以空格分割。 ai 代表参与游戏的数字的值, pi 代表度度熊为该数字指定的位置,如果 pi=1 ,代表该数字的位置不被限制。度度熊保证不会为两个数字指定相同的位置。
 

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

第二行输出数字重新排列后最大的所有相邻两数乘积的和,即 max{a1a2+a2a3+......+aN1aN}
 

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
 

Sample Output
  
  
Case #1: -70 Case #2: 4600
 
题意
:略;

思路

设dp[i][j]:表示在 i 状态下,以第 j 个数结尾的最优解。

在放完第i-1位,准备放第i位的时候,对结果有影响的只有第i-1位上的数的值*第i位上的数的值(a[i-1]*a[i])。


=>枚举第i位,对于每个第i位的数,枚举第i-1位即可。

=>或枚举第i-1位,对于每个第i-1位的数,枚举第i位即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;

const int INF=1e18+7;

typedef struct{
    int num;
    int pos;
}Point;

int a[20]; 
int p[20];
long long dp[1<<17][20];
int fix[20]; //fix[i]:第i位若固定,fix[i]等于固定的这个数在a[ ]中的下标,若不固定,则为-1

int main()
{
    int T;
    int Case=1;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        memset(fix,-1,sizeof(fix));
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&a[i],&p[i]);
            if(p[i] != -1)
                fix[p[i]] = i;
        }
        
        //dp[ ][ ]初始化
        for(int i=0;i<(1<<n);i++)
        {
            for(int j=0;j<n;j++)
                dp[i][j] = -INF;
        }
        
        //考虑第0位的情况
       if(fix[0] != -1)
       {
            dp[1<<fix[0]][fix[0]]=0;  //第0位固定
       }
       else
       {
            for(int i=0;i<n;i++) //第0位不固定的话,所有p=-1的数都可以放在第0位
                if(p[i]==-1)
                    dp[1<<i][i]=0;
       }
       
       for(int i=1;i< (1<<n);i++)
       {
            //num表示第i种状态里有多少个1,即已确定了多少位
            int num=0;
            for(int j=0;j<n;j++)
            {
                if(i&(1<<j))
                    num++;
            }
            //若第num位固定
            if(fix[num]!= -1)
            {
                for(int j=0;j<n;j++)
                {
                    //不需要担心已经固定的数会不会被拿来随意放,因为其dp[i][j]= - INF,下同
                    if((i&(1<<j))&&j != fix[num])
                        dp[i|(1 << fix[num])][fix[num]]=max(dp[i|(1 << fix[num])][fix[num]] , dp[i][j]+a[j]*a[fix[num]]);
                }
            }
            //若第num位不固定
            else
            {
                //枚举第j位
                for(int j=0;j<n;j++)
                {
                    //j为此i状态中已有的数
                    if(i&(1<<j))
                    {
                        //枚举第j+1位
                        for(int k=0;k<n;k++)
                        {
                            //k为此i状态中还没有的数
                            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;
       for(int i=0;i<n;i++)
       {
            //题目要求的最大值为把所有点都考虑的状态时的最大值,即为所有数状态都为1时的状态,即(1<<n)-1时
            ans=max(ans,dp[(1<<n)-1][i]);
       }
       printf("Case #%d:\n",Case++);
       printf("%d\n",ans);
    }
    return 0;
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值