数字重排

原题链接

数字重排

有n个整数排成一排,有一些数字被固定在某些位置,另外的一些可以自由交换,最终达到所有相邻两数乘积的和最大的目的。

输入

第一行输入一个整数 (1 \le n \le 16) (1n16)。

接下来nn行,每行给出两个整数a_i(−10^4 \le a_i \le 10^4)、p_i(-1 \le p_i \lt n)ai(104ai104)pi(1pi<n),以空格分割。

a_iai表示数字的值,p_ipi为该数字指定的位置,如果p_i = −1pi=1,代表该数字的位置不被限制。输入保证不会为两个数字指定相同的位置。

输出

数字重新排列后最大的所有相邻两数乘积的和,即\max \{a_1*a_2+a_2*a_3+......+a_{n−1} * a_n \}max {a1a2+a2a3+......+an1an}。

样例

输入

复制
6
-1 0
2 1
-3 2
4 3
-5 4
6 5

输出

复制
-70

输入

复制
5
40 -1
50 -1
30 -1
20 -1
10 -1

输出

复制
4600

提示

子任务1,30分, 1 \le n \le 9 1n9

子任务2,70分,全范围。

分析

本题涉及多个状态

  • 数字的选择情况(选择了哪几个数字)
  • 数字放置的位置情况
  • 每次放的最后一个数字是什么
  • ......

但是不能开两维状态压缩(1<<16)*(1<<16)空间会爆

仔细想想,按照固定的从左向右放的规则,如果数字的选择情况已知,那么放置情况就不用单独考虑了

转移的时候只需要知道当前的最后一位数字就可以了,那么空间就是(1<<16)*16可以承受

不过细节还是很多的,比如各种初始化和边界

代码

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int n;
int dp[1<<20][20];
int put[20],num[20],a[20],p[20];
//第一维---状态:放了哪几个数
//第二维---当前状态的最后一个数 
void checkmax(int &x,int y)
{
    if(x==-INF||x<y)
    {    
        x=y;
    }
} 
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d%d",&a[i],&p[i]);
        if(p[i]!=-1) put[p[i]]=a[i],num[p[i]]=i;  
    }
    for(int i=0;i<(1<<n);i++)    
        for(int j=0;j<n;j++)
            dp[i][j]=-INF;//初始化成-INF,因为要取max 
    if(put[0]) 
    {
        for(int i=0;i<n;i++)
            if(p[i]==0)
            dp[1<<i][i]=0;
    }
    else 
        for(int i=0;i<n;i++)
            if(p[i]==-1)
            dp[1<<i][i]=0;
    //第一个数怎么放置,特殊处理一下 
    for(int mask=0;mask<(1<<n);mask++)
    {
        int cnt=0;
        for(int i=0;i<n;i++)
            if( (1<<i)&mask ) cnt++;
        if(put[cnt]) 
        {
            for(int lst=0;lst<n;lst++)//上一个是什么 
                if(dp[mask][lst]!=-INF)
                checkmax(dp[mask|(1<<num[cnt])][num[cnt]],dp[mask][lst]+put[cnt]*a[lst]);
        }
        else
            for(int i=0;i<n;i++)//现在选什么 
            {
                if( !(mask&(1<<i)) &&p[i]==-1)
                    for(int lst=0;lst<n;lst++)//上一个是什么 
                        if(dp[mask][lst]!=-INF)
                        checkmax(dp[mask|(1<<i)][i],dp[mask][lst]+a[lst]*a[i]);
            }
    }
    int ans=-INF;
    for(int i=0;i<n;i++)
    {
        ans=max(ans,dp[(1<<n)-1][i]);
        
    }
    printf("%d",ans);
    return 0;
}

 

 

posted @ 2021-05-04 14:14  conprour  阅读(227)  评论(0编辑  收藏  举报