【CF1342F】 Make It Ascending

题目

题目描述
You are given an array aa consisting of nn elements. You may apply several operations (possibly zero) to it.

During each operation, you choose two indices ii and jj ( 1 \le i, j \le n1≤i,j≤n ; i \ne ji


=j ), increase a_ja
j

by a_ia
i

, and remove the ii -th element from the array (so the indices of all elements to the right to it decrease by 11 , and nn also decreases by 11 ).

Your goal is to make the array aa strictly ascending. That is, the condition a_1 < a_2 < \dots < a_na
1

<a
2

<⋯<a
n

should hold (where nn is the resulting size of the array).

Calculate the minimum number of actions required to make the array strictly ascending.

输入格式
The first line contains one integer TT ( 1 \le T \le 100001≤T≤10000 ) — the number of test cases.

Each test case consists of two lines. The first line contains one integer nn ( 1 \le n \le 151≤n≤15 ) — the number of elements in the initial array aa .

The second line contains nn integers a_1a
1

, a_2a
2

, …, a_na
n

( 1 \le a_i \le 10^61≤a
i

≤10
6
).

It is guaranteed that:

the number of test cases having n \ge 5n≥5 is not greater than 50005000 ;
the number of test cases having n \ge 8n≥8 is not greater than 500500 ;
the number of test cases having n \ge 10n≥10 is not greater than 100100 ;
the number of test cases having n \ge 11n≥11 is not greater than 5050 ;
the number of test cases having n \ge 12n≥12 is not greater than 2525 ;
the number of test cases having n \ge 13n≥13 is not greater than 1010 ;
the number of test cases having n \ge 14n≥14 is not greater than 33 ;
the number of test cases having n \ge 15n≥15 is not greater than 11 .
输出格式
For each test case, print the answer as follows:

In the first line, print kk — the minimum number of operations you have to perform. Then print kk lines, each containing two indices ii and jj for the corresponding operation. Note that the numeration of elements in the array changes after removing elements from it. If there are multiple optimal sequences of operations, print any one of them.

题意翻译
给予一个包含nn个元素的数组aa,你可以进行以下操作:

选择两个不同的元素a_i,a_ja
i

,a
j

(1 \le i,j \le n1≤i,j≤n,i \ne ji


=j)
将a_ja
j

的值加上a_ia
i

,并移除aa中的第ii个元素。
求使aa数组严格递增(对于1 \le i < n1≤i<n,有a_i<a_{i+1}a
i

<a
i+1

)所需的最少操作数(可以为00)。

输入有多组数据(1 \le T \le 100001≤T≤10000),且保证:

1 \le a_i\le 10^61≤a
i

≤10
6

1 \le n \le 151≤n≤15
在单个测试点中,满足以下条件的数据与出现次数tt 的关系满足:
n \gen≥ t \let≤
55 50005000
88 500500
1010 100100
1111 5050
1212 2525
1313 1010
1414 33
1515 11
例如:n=15n=15的数据在一个测试点中至多出现一次。

输入输出样例
输入 #1复制
4
8
2 1 3 5 1 2 4 5
15
16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1
2
3 3
14
1 2 3 4 5 6 7 8 9 10 11 12 13 14
输出 #1复制
3
6 8
1 6
4 1
7
1 15
1 13
1 11
1 9
1 7
1 5
1 3
1
2 1
0
说明/提示
In the first test case, the sequence of operations changes aa as follows:
[2,1,3,5,1,2,4,5]→[2,1,3,5,1,4,7]→[1,3,5,1,6,7]→[2,3,5,6,7] .

思路

试图把一个序列变得单调很难,所以我们直接求解结果序列。下面我们把结果序列的一个元素看成一个原序列元素的集合。
设f[i][p][s]是“当前已经决策了前 i 个集合,且第 i 个集合以原序列的第 p 个元素为基础(所有其他元素都合并到它),已经使用的原序列元素集合是 s”时,第 i 个集合的最小值。
每次转移时枚举一个没用过的原序列元素,并入第 i 个集合或者新建第 i+1 个集合。注意保证第 i 个集合的基础一定要小于第 i+1 个集合的基础。复杂度O(n23n)

输出方案?只需要保存一下转移路径就可以了。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std; 

struct ND{
	int i,p,s; 
}; 

int T,N; 
int A[15]; 
int sum[1<<15]; 
int F[16][16][1<<15]; ND fa[15][15][1<<15];  

void update(ND u,ND v,int k)
{
	F[v.i][v.p][v.s]=min(F[v.i][v.p][v.s],k); 
	if(F[v.i][v.p][v.s]==k) fa[v.i][v.p][v.s]=u; 
}

int id[15]; 

int main()
{
	scanf("%d",&T); 
	while(T--)
	{
		scanf("%d",&N); 
		for(int i=0; i<N; i++) scanf("%d",&A[i]); 
		for(int s=0; s<(1<<N); s++){
			sum[s]=0; 
			for(int i=0; i<N; i++) if((s&(1<<i))!=0)
				sum[s]+=A[i]; 
		}
		
		for(int i=0; i<=N; i++)
		for(int p=0; p<=N; p++)
		for(int s=0; s<(1<<N); s++)
			F[i][p][s]=0x3f3f3f3f; 
		F[0][0][0]=0; 
		for(int i=0; i<N; i++)
		for(int p=0; p<N; p++)
		for(int s=0; s<(1<<N); s++) if(F[i][p][s]!=0x3f3f3f3f){
			ND u=(ND){i,p,s};  
			int ns=((1<<N)-1)^s; 
			for(int s0=ns; s0; s0=(s0-1)&ns) if(sum[s0]>F[i][p][s]&&(s0>>p)!=0){
				ND v=(ND){i+1,p+1+__builtin_ctz(s0>>p),s|s0}; 
				update(u,v,sum[s0]); 
			}
		}
		
		ND ans=(ND){-1,-1,-1}; 
		for(int i=N; i>=1; i--){
			for(int p=1; p<=N; p++)
				if(F[i][p][(1<<N)-1]!=0x3f3f3f3f){ans=(ND){i,p,(1<<N)-1}; break; }	
			if(ans.i!=-1) break; 
		}
		printf("%d\n",N-ans.i); 
		for(int i=0; i<N; i++) id[i]=i+1; 
		while(ans.i!=0){
			ND par=fa[ans.i][ans.p][ans.s]; 
			int s0=par.s^ans.s; 
			for(int i=0; i<N; i++) if((s0&(1<<i))!=0&&i!=ans.p-1){
				printf("%d %d\n",id[i],id[ans.p-1]); 
				for(int j=i+1; j<N; j++) id[j]--; 
			}
			ans=par; 
		}
	}
}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值