JZOJ7月21日提高组T4 WTF交换

题目

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<N, -10000<=A[i]<=10000。

题解

题意

有一种叫做WTF交换的东东(具体见题面)
现在给出数组 A A A R R R N N N,求最大的 s u m sum sum和满足最大 s u m sum sum I D ID ID数组(任意一种即可)

分析

DP
f [ i ] [ j ] f[i][j] f[i][j]表示 I D ID ID数组的第 i i i位选择 j j j的最大 s u m sum sum

第一行

最初转移

枚举一个 k ( 1 k(1 k(1~ n − 1 ) n-1) n1)
f [ i ] [ j ] = m a x ( f [ i − 1 ] [ k ] + a [ g e t ( m i n ( j , k ) ) ] − a [ g e t ( m a x ( j , k ) + 1 ) ] ) f[i][j]=max(f[i-1][k]+a[get(min(j,k))]-a[get(max(j,k)+1)]) f[i][j]=max(f[i1][k]+a[get(min(j,k))]a[get(max(j,k)+1)])
何为 g e t get get
若当前选择第 i i i个位置
那么之前肯定进行过 i − 1 i-1 i1次WTF操作
那么这个 j ( k ) j(k) j(k)肯定会在数组内调换位置
g e t get get操作就是把一开始 j ( k ) j(k) j(k)的位置找到
显而易见,这个转移是 O ( n 3 ) O(n^3) O(n3),只能拿60%的数据

优化

分类讨论
k ≤ j k≤j kj时:
原方程变为: f [ i ] [ j ] = m a x ( f [ i − 1 ] [ k ] + a [ g e t ( k ) ] − a [ g e t ( j + 1 ) ] ) f[i][j]=max(f[i-1][k]+a[get(k)]-a[get(j+1)]) f[i][j]=max(f[i1][k]+a[get(k)]a[get(j+1)])
可以看出, f [ i − 1 ] [ k ] + a [ g e t ( k ) ] f[i-1][k]+a[get(k)] f[i1][k]+a[get(k)]是和 j j j没有关系的
既然要让答案最大,那么 f [ i − 1 ] [ k ] + a [ g e t ( k ) ] f[i-1][k]+a[get(k)] f[i1][k]+a[get(k)]就应该最大
那么就可以设一个 s s s表示最大的 f [ i − 1 ] [ k ] + a [ g e t ( k ) ] f[i-1][k]+a[get(k)] f[i1][k]+a[get(k)]
至于怎么满足 k ≤ j k≤j kj,就可以让 j j j顺着扫一遍,那么最大的 s s s所对应的 k k k肯定是在 1 1 1~ j j j里的
手动模拟过程:

  1. 新的 j j j进入
  2. 判断 f [ i − 1 ] [ j ] + a [ g e t ( j ) ] f[i-1][j]+a[get(j)] f[i1][j]+a[get(j)] s s s的大小,更新 s s s
  3. 判断 s − a [ g e t ( j + 1 ) ] s-a[get(j+1)] sa[get(j+1)] f [ i ] [ j ] f[i][j] f[i][j]的大小,更新 f [ i ] [ j ] f[i][j] f[i][j]

j ≤ k j≤k jk时:
原方程变为: f [ i ] [ j ] = m a x ( f [ i − 1 ] [ k ] + a [ g e t ( j ) ] − a [ g e t ( k + 1 ) ] ) f[i][j]=max(f[i-1][k]+a[get(j)]-a[get(k+1)]) f[i][j]=max(f[i1][k]+a[get(j)]a[get(k+1)])
可以看出, f [ i − 1 ] [ k ] − a [ g e t ( k + 1 ) ] f[i-1][k]-a[get(k+1)] f[i1][k]a[get(k+1)]是和 j j j没有关系的
既然要让答案最大,那么 f [ i − 1 ] [ k ] − a [ g e t ( k + 1 ) ] f[i-1][k]-a[get(k+1)] f[i1][k]a[get(k+1)]就应该最大
那么就可以设一个 s s s表示最大的 f [ i − 1 ] [ k ] − a [ g e t ( k + 1 ) ] f[i-1][k]-a[get(k+1)] f[i1][k]a[get(k+1)]
至于怎么满足 j ≤ k j≤k jk,就可以让 j j j倒着扫一遍,那么最大的 s s s所对应的 k k k肯定是在 j j j~ n − 1 n-1 n1里的
手动模拟过程:

  1. 新的 j j j进入
  2. 判断 f [ i − 1 ] [ j ] − a [ g e t ( j + 1 ) ] f[i-1][j]-a[get(j+1)] f[i1][j]a[get(j+1)] s s s的大小,更新 s s s
  3. 判断 s + a [ g e t ( j ) ] s+a[get(j)] s+a[get(j)] f [ i ] [ j ] f[i][j] f[i][j]的大小,更新 f [ i ] [ j ] f[i][j] f[i][j]

原谅我偷懒
那么最大的 s u m sum sum就是最大的 f [ n ] [ i ] f[n][i] f[n][i]
这时第一行求处理完了

第二行

处理第二行,其实就是要记每个 f [ i ] [ j ] f[i][j] f[i][j]是由哪个 k k k转移过来的
开一个新的数组 g [ i ] [ j ] g[i][j] g[i][j]表示 I D ID ID数组的第 i i i位选择 j j j的最大 s u m sum sum是从哪个 k k k转移过来的
在上述的更新 f [ i ] [ j ] f[i][j] f[i][j]的那一步里顺便更新 g [ i ] [ j ] g[i][j] g[i][j]
注意输出的时候,因为我们是由 g [ 1 ] [ ] g[1][] g[1][]转移到 g [ 2 ] [ ] g[2][] g[2][]转移到 g [ 3 ] [ ] g[3][] g[3][]……最后转移到 g [ n ] [ ] g[n][] g[n][],所以要 d g dg dg输出
先别急着去打
发现,这里的 g [ i ] [ j ] g[i][j] g[i][j]只有 n n n个数
但题目要求的是 n + 1 n+1 n+1个数
那么第 n + 1 n+1 n+1个数是什么???
其实就是最大 f [ n ] [ i ] f[n][i] f[n][i] i i i

Code

#include<cstdio>
#include<iostream>
#define inf 1234567890
#define get(x) ((((x)-(i-1)*r)%n+n-1)%n+1)
using namespace std;
int n,r,i,j,s,ss,t,ans,a[3005],f[3005][3005],g[3005][3005];
void dg(int x,int before)
{
	if (x!=0) dg(x-1,g[x][before]);
	printf("%d ",before);
}
int main()
{
	scanf("%d%d",&n,&r);
	for (i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (i=1;i<=n;i++)
	{
		ss=-inf;
		for (j=1;j<n;j++)
		{
			s=f[i-1][j]+a[get(j)];
			if (s>ss) 
			{
				ss=s;
				t=j;	
			}
			if (ss-a[get(j+1)]>f[i][j])
			{
				f[i][j]=ss-a[get(j+1)];
				g[i][j]=t;
			}
		}
		ss=-inf;
		for (j=n-1;j>=1;j--)
		{
			s=f[i-1][j]-a[get(j+1)];
			if (s>ss) 
			{
				ss=s;
				t=j;	
			}
			if (ss+a[get(j)]>f[i][j])
			{
				f[i][j]=ss+a[get(j)];
				g[i][j]=t;
			}	
		}
	}
	ans=-inf;
	for (i=1;i<n;i++)
		if (f[n][i]>ans)
		{
			ans=f[n][i];
			t=i;
		}
	printf("%d\n",ans);
	dg(n,t);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值