度度熊是他同时代中最伟大的数学家,一切数字都要听命于他。现在,又到了度度熊和他的数字仆人们玩排排坐游戏的时候了。游戏的规则十分简单,参与游戏的N个整数将会做成一排,他们将通过不断交换自己的位置,最终达到所有相邻两数乘积的和最大的目的,参与游戏的数字有整数也有负数。度度熊为了在他的数字仆人面前展现他的权威,他规定某些数字只能在坐固定的位置上,没有被度度熊限制的数字则可以自由地交换位置。
Input
第一行一个整数TT,表示TT组数据。
每组测试数据将以如下格式从标准输入读入:
NN
a1p1a1p1
a2p2a2p2
:
aNPNaNPN
第一行,整数 N(1≤N≤16)N(1≤N≤16),代表参与游戏的整数的个数。
从第二行到第 (N+1)(N+1) 行,每行两个整数,ai(−10000≤ai≤10000)ai(−10000≤ai≤10000)、pi(pi=−1pi(pi=−1 或 0≤pi<N)0≤pi<N),以空格分割。aiai代表参与游戏的数字的值,pipi代表度度熊为该数字指定的位置,如果pi=−1pi=−1,代表该数字的位置不被限制。度度熊保证不会为两个数字指定相同的位置。
Output
第一行输出:"Case #i:"。ii代表第ii组测试数据。
第二行输出数字重新排列后最大的所有相邻两数乘积的和,即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
当数据n在20以内,可以考虑状态压缩。
因为放的数是放一个,放二个,放三个......,放完n个(即11111....11共n个1,结束)
所以可以用int代表的二进制表示当前放的状态。如 1001,表示已经放好第0个,第3个数了。
dp[i][j]:表示当前放的数状态是i,以第j个数结尾的最大值。
则从当前状态dp[i][j]找打下一个没有被放过的数放好进行状态转移。
若nxt能放,则dp[i|(1<<nxt)][nxt]=max(dp[i|(1<<nxt)],dp[i][j]+a[j]*a[nxt]);
#include<iostream>
#include<cstring>
using namespace std;
int a[20],p[20],vis[20],dp[1<<20][20];
const int INF=1600000000;
void checkmax(int &x,int y){
if(x<y)
x=y;
}
int main()
{
int t,c=0,n;
cin>>t;
while(t--){
cin>>n;
memset(vis,0,sizeof(vis));
for(int i=0; i<n; i++){
cin>>a[i]>>p[i];
if(p[i]!=-1){
vis[p[i]]=1;//记录哪个位置被放过
}
}
for(int i=0; i<(1<<n); i++){
for(int j=0; j<n; j++){
dp[i][j]=-INF;
}
}
//初始化,看0位置是否被放过
if(vis[0]){//假如0位置已经被指定数
for(int i=0; i<n; i++){
if(p[i]==0){
dp[1<<i][i]=0;
}
}
}
else{//0位置没有放过,则可以放任何没有被放过的数
for(int i=0; i<n; i++){
if(p[i]==-1){
dp[1<<i][i]=0;
}
}
}
for(int mask=0; mask<(1<<n); mask++){//放数的状态,一个一个数地放,直到1111...即所又的数放好
for(int end=0; end<n; end++){
if(dp[mask][end]!=-INF)//当前状态合法
{
int pos=0;
for(int i=0; i<n; i++){
if(mask&(1<<i)) pos++;//记录已经放了几个数
}
for(int nxt=0; nxt<n; nxt++){//找到下一个数应该放在pos位置上
if(!(mask&(1<<nxt)))//该位置还没有被放过数
{
//假如第nxt个数没有被限制且pos位置没有放数或者正好被限制放在pos位置上
if(p[nxt]==pos|| (vis[pos]==0&&p[nxt]==-1))
{
checkmax(dp[mask|(1<<nxt)][nxt],dp[mask][end]+a[end]*a[nxt]);
}
}
}
}
}
}
int ans=-INF;
for(int end=0; end<n; end++){
checkmax(ans,dp[(1<<n)-1][end]);
}
c++;
cout<<"Case #"<<c<<":"<<endl<<ans<<endl;
}
return 0;
}