E-Longest Increasing Subsequence
题目大意:
构造一个序列,要求序列中最长上升子序列的数量为n。
思路:
n可以用二进制表示成bk bk-1 …b1
对于bk,可以构造一个2 1 4 3 … 2*k 2*k-1这样一个序列,对于剩下的位bi,首先在序列2*i的位置后面插入一个大于2*k的数字,但是这样构造出来上升序列长度并不是k,因此还要在位置之后插入一个上升序列,这个上升序列的长度等于该位往上0的个数,选择合适的数,多个位置就能共用这个上升序列。
对于n=1、2、3的情况要进行特殊处理。
AC代码:
#include <bits/stdc++.h>
const int N = 1e2 + 5;
using namespace std;
vector<int> v[N], bits;
void solve()
{
for (int i = 0; i < N; i++)
v[i].clear();
bits.clear();
int n, m;
cin >> n;
m = n;
for (int i = 0; m; i++)
{
if (m & (1 << i))
{
bits.push_back(i);
m -= (1 << i);
}
}
if (n == 1)
cout << "2\n1 2\n";
else if (n == 2)
cout << "3\n2 1 3\n";
else if (n == 3)
cout << "4\n2 4 1 3\n";
else
{
m = bits.back() * 2;
for (int i = 1; i <= bits.back(); i++)
{
v[i].push_back(i * 2);
v[i].push_back(i * 2 - 1);
}
for (int i = 0; i < bits.size() - 1; i++)
{
int len = bits[i + 1] - bits[i];
while (len--)
v[bits[i]].push_back(++m);
}
cout << m << "\n";
for (int i = 0; i < N; i++)
for (auto e : v[i])
cout << e << " ";
cout << "\n";
}
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
solve();
return 0;
}
I-The Great Wall II
题目大意:
有一个长度为n的数列a,将数列分成m段,使得每段中的最大值之和最小。求出m为1~n时的所有值。
前置知识:单调栈
思路:
可以用一个单调栈来维护状态,单调栈中的状态相当于将一个区间中相同的情况进行了压缩。
过程比较绕,因此下面的解释可能有误。
f[i][j]表示将前j个数字分成i段的最小值
性质1:当一个区间的左端点或右端点定下来时,扩大区间的长度,区间中的最大值不会减小,只会保持不变或增大。
对于f[i][j+1]。
情况1:如果a[j+1] <= a[j],那么把a[j+1]归到f[i][j]的最后一段中,结果不变。
比如1,2,3 2的分法和1,2,3的分法结果一样,也就是最后一个2不管加不加对结果都没有影响。
如果a[j+1]单独成为一段,那么f[i][j+1]的值就是f[i-1][j]+a[j+1]
因此当a[j+1] < a[j]时,f[i][j+1]= min(f[i][j],f[i-1][j]+a[j+1])
情况2:如果a[j+1] > a[j],将a[j]归并到f[i][j+1]的最后一段中,最后一段的值不会改变。
因此这时候就一直往前搜索,搜索的同时要保证前面剩余的个数要不少于i-1,因为每段至少要有一个数字。
直到不能再往前或是出现一个不小于a[j+1]的值为止。搜索的过程中记录每个数字合并到最后一段后其左侧的最小f值。
于是又回到了情况1。
AC代码:
#include <bits/stdc++.h>
const int inf = 1e9 + 7;
const int N = 8e3 + 5;
using namespace std;
struct node
{
int a, val, m;
};
int f1[N], f2[N], a[N];
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
f1[i] = max(f1[i - 1], a[i]); //分成一段的情况,就是找最大的a
}
cout << f1[n] << "\n";
for (int i = 2; i <= n; i++) //将数列分成i段
{
stack<node> s;
s.push({inf, inf, inf}); //防止栈中没有元素时访问出错
for (int j = i; j <= n; j++)
{
int res = f1[j - 1];
while (s.top().a < a[j])
{
res = min(res, s.top().val);
s.pop();
}
s.push({a[j], res, min(s.top().m, res + a[j])});
f2[j] = s.top().m;
}
for (int j = i; j <= n; j++)
f1[j] = f2[j];
cout << f2[n] << "\n";
}
return 0;
}