【JZOJ B组】【NOIP2015模拟10.22】WTF交换

本文介绍了一种名为Warshall-Turing-Fourier(WTF)的数组变换算法,并提出了一种动态规划的方法来解决给定数组经过该变换后变量sum可能出现的最大值的问题。通过构建动态规划的状态转移方程,实现了在满足特定条件下的ID数组构造。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

假定给出一个包含N个整数的数组A,包含N+1个整数的数组ID,与整数R。其中ID数组中的整数均在区间[1,N-1]中。
用下面的算法对A进行Warshall-Turing-Fourier变换(WTF):

sum = 0
for i = 1 to N
     index = min{ ID[i], ID[i+1] }
     sum = sum + A[index]
     将数组A往右循环移动R位
将数组A内所有的数取相反数
for i = 1 to N
     index = max{ ID[i], ID[i+1] }
     index = index + 1
     sum = sum + A[index]
     将数组A往右循环移动R位

给出数组A以及整数R,但没有给出数组ID。在对数组A进行了WTF算法后,变量sum的可能出现的最大值数多少?

Input

第一行包含两个整数N与R。
第二行包含N个整数,代表A[1]到A[N]的值。

Output

第一行输出变量sum可能出现的最大值。
第二行输出此时的ID数组,包含N+1个整数。必须满足ID数组中每一个数均在区间[1,N-1]中。若有多个满足的ID数组,输出任意一组。
如果第一行是正确的(不管有没有输出第二行),你能得到该测试点50%的得分。

Sample Input

输入1:
5 3
1 -1 1 -1 1
输入2:
6 5
2 5 4 1 3 5

Sample Output

输出1:
10
1 1 1 2 2 3
输出2:
16
3 2 1 1 5 4 1

Data Constraint

对于20%的数据,N<=7。
对于60%的数据,N<=300。
对于100%的数据,2<=N<=3000, 1<=R

思路

可以发现一个ID只和上一个ID与下一个ID有关,考虑DP。

设f[i][j]表示第i个ID填j最大的sum。

当k>j
f[i][j]=maxf[i−1][k]+a[min(j,k)]−a[max(j,k)+1]

当k<=j
f[i][j]=maxf[i−1][k]+a[k]−a[j+1]

注意一下a值变换
还有要用g[i][j] 记录前驱

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f3f,maxn=3077;
int n,r,a[maxn],f[maxn][maxn],g[maxn][maxn],ass,t;
void print(int x,int y)
{
    if(x!=0) print(x-1,g[x][y]);
    printf("%d ",y+1);
}
int main()
{
    memset(f,200,sizeof(f));
    memset(f[0],0,sizeof(f[0]));
    scanf("%d%d",&n,&r);
    for(int i=0; i<n; i++) scanf("%d",&a[i]);
    for(int i=1; i<=n; i++)
    {
        ass=-inf;
        for(int j=0; j<n-1; j++)
        {
            if(f[i-1][j]+a[((j-(i-1)*r)%n+n)%n]>ass)
            {
                ass=f[i-1][j]+a[((j-(i-1)*r)%n+n)%n];
                t=j;
            }
            if(ass-a[((j+1-(i-1)*r)%n+n)%n]>f[i][j])
            {
                f[i][j]=ass-a[((j+1-(i-1)*r)%n+n)%n];
                g[i][j]=t;
            }
        }
        ass=-inf;
        for(int j=n-2; j>=0; j--)
        {
            if(f[i-1][j]-a[((j+1-(i-1)*r)%n+n)%n]>ass)
            {
                ass=f[i-1][j]-a[((j+1-(i-1)*r)%n+n)%n];
                t=j;
            }
            if(ass+a[((j-(i-1)*r)%n+n)%n]>f[i][j])
            {
                f[i][j]=ass+a[((j-(i-1)*r)%n+n)%n];
                g[i][j]=t;
            }
        }
    }
    ass=-inf;
    for(int j=0; j<n-1; j++) if(f[n][j]>ass)
    {
        ass=f[n][j];
        t=j;
    }
    printf("%d\n",ass);
    print(n,t);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值