题目链接:点我啊╭(╯^╰)╮
参考博客:点我啊╭(╯^╰)╮
题目大意:
n
n
n 个数字,要求用最少的操作
每次操作选择
i
i
i 和
j
j
j,
a
j
+
=
a
i
a_j += a_i
aj+=ai
最后生成单调递增的序列
并输出过程
解题思路:
可以看出是分组选,每一组合并成一个值
d
p
[
i
]
[
j
]
[
z
]
dp[i][j][z]
dp[i][j][z] 表示 到第
i
i
i 组为止,选取的状态为
j
j
j,最右边的值位置为
z
z
z 的最小值
从
d
p
[
i
]
[
j
]
[
z
]
dp[i][j][z]
dp[i][j][z] 到
d
p
[
i
+
1
]
dp[i+1]
dp[i+1] 转移,
i
+
1
i+1
i+1可选的状态即为
(
(
1
<
<
n
)
−
1
)
⨁
j
( (1<<n) - 1 ) \bigoplus j
((1<<n)−1)⨁j 的所有子集
并且
d
p
[
i
+
1
]
dp[i+1]
dp[i+1] 的
z
z
z 值一定要比
d
p
[
i
]
dp[i]
dp[i] 的
z
z
z 值大
因此枚举所有子集就可以求出所有最小值
最后的答案肯定是从组数最多,并且
z
z
z 值最大 的那种情况
时间复杂度:
O
(
n
2
×
3
n
)
O(n^2 \times 3^n)
O(n2×3n)
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
int T, n, a[17], sum[1<<16];
int dp[17][1<<16][17];
pii op[17][1<<16][17];
signed main() {
scanf("%d", &T);
while(T--){
scanf("%d", &n);
for(int i=0; i<n; i++) scanf("%d", a+i);
for(int i=0; i<=n; i++)
for(int j=0; j<1<<n; j++)
for(int z=0; z<=n; z++)
sum[j] = 0, dp[i][j][z] = 2e9;
for(int s=1; s<1<<n; s++)
for(int i=0; i<n; i++)
if((s >> i) & 1) sum[s] += a[i];
dp[0][0][0] = 0;
for(int i=1; i<=n; i++)
for(int j=0; j<1<<n; j++)
for(int z=0; z<n; z++){
int s = (1 << n) - 1 ^ j;
if(dp[i-1][j][z] > 1e9) continue;
for(int k=s; k; k=(k-1)&s){
if(sum[k] <= dp[i-1][j][z]) continue;
if(k >> z == 0) continue;
int nz = z + __builtin_ffs(k>>z);
if(dp[i][j|k][nz] > sum[k]){
dp[i][j|k][nz] = sum[k];
op[i][j|k][nz] = {j, z};
}
}
}
int a = -1, b = (1 << n) - 1, c = -1;
for(int i=n; i; i--){
for(int j=n; j; j--)
if(dp[i][b][j]<1e9){
c = j; break;
}
if(c >= 0) {
a = i; break;
}
}
vector <pii> ans;
for(int i=a; i; i--){
int nb = b ^ op[i][b][c].first;
int nc = op[i][b][c].second;
for(int j=0; j<n; j++)
if(((nb >> j) & 1) && j != c - 1)
ans.push_back({j, c-1});
b = op[i][b][c].first, c = nc;
}
printf("%d\n", ans.size());
for(int i=0; i<ans.size(); i++){
int x = ans[i].first, y = ans[i].second;
for(int j=0; j<i; j++) if(ans[j].first < ans[i].first) x--;
for(int j=0; j<i; j++) if(ans[j].first < ans[i].second) y--;
printf("%d %d\n", x + 1, y + 1);
}
};
}