• 数字重排


    原题链接

    数字重排

    有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;
    }
  • 相关阅读:
    《Scrum实战》第0次课【如何学习敏捷】全团课后任务汇总
    关于 Android 5.0 原生系统网络图标上的感叹号问题解决方法
    [Android Tips] 18. Enable/Disable WiFi via adb
    [Android Tips] 17. 查看 APK 签名信息
    [Android Tips] 16. Update Android SDK from command-line
    [Android Tips] 15. Enforcing spaces in string resources
    Gradle Cheat Sheet
    [Android Tips] 14. Using Proguard with Android without obfuscation
    cuda线程/线程块索引小结
    cuda编程学习6——点积dot
  • 原文地址:https://www.cnblogs.com/conprour/p/14729404.html
Copyright © 2020-2023  润新知