Sitting in Line HDU - 5691

度度熊是他同时代中最伟大的数学家,一切数字都要听命于他。现在,又到了度度熊和他的数字仆人们玩排排坐游戏的时候了。游戏的规则十分简单,参与游戏的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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值