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(1≤N≤16) ,代表参与游戏的整数的个数。
从第二行到第 (N+1) 行,每行两个整数, ai(−10000≤ai≤10000) 、 pi(pi=−1 或 0≤pi<N) ,以空格分割。 ai 代表参与游戏的数字的值, pi 代表度度熊为该数字指定的位置,如果 pi=−1 ,代表该数字的位置不被限制。度度熊保证不会为两个数字指定相同的位置。
每组测试数据将以如下格式从标准输入读入:
N
a1p1
a2p2
:
aNPN
第一行,整数 N(1≤N≤16) ,代表参与游戏的整数的个数。
从第二行到第 (N+1) 行,每行两个整数, ai(−10000≤ai≤10000) 、 pi(pi=−1 或 0≤pi<N) ,以空格分割。 ai 代表参与游戏的数字的值, pi 代表度度熊为该数字指定的位置,如果 pi=−1 ,代表该数字的位置不被限制。度度熊保证不会为两个数字指定相同的位置。
Output
第一行输出:"Case #i:"。
i
代表第
i
组测试数据。
第二行输出数字重新排列后最大的所有相邻两数乘积的和,即 max{a1⋅a2+a2⋅a3+......+aN−1⋅aN} 。
第二行输出数字重新排列后最大的所有相邻两数乘积的和,即 max{a1⋅a2+a2⋅a3+......+aN−1⋅aN} 。
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;
}