C. Sereja and Swaps
time limit per test 1second
memory limit per test 256megabytes
As usual, Sereja has array a, its elementsare integers: a[1], a[2], ..., a[n]. Let'sintroduce notation:
A swap operation is the followingsequence of actions:
· choose two indexes i, j (i ≠ j);
· perform assignments tmp = a[i], a[i] = a[j], a[j] = tmp.
What maximum value of function m(a) can Sereja getif he is allowed to perform at most k swap operations?
Input
The first line contains two integers n and k (1 ≤ n ≤ 200; 1 ≤ k ≤ 10). The next linecontains n integers a[1], a[2], ..., a[n] ( - 1000 ≤ a[i] ≤ 1000).
Output
In a single line print the maximum value of m(a) that Sereja canget if he is allowed to perform at most k swap operations.
Examples
Input
10 2
10 -1 2 2 2 2 2 2 -1 10
Output
32
Input
5 10
-1 -1 -1 -1 -1
Output
-1
【题意】
给定一个长度为 n 的序列 a1,an,...,an,你有 k 次机会选择 一个 x 和 y(1 ≤x < y≤n),交换 ax 和 ay。 k 次机会不一定要全部用完,你需要操作完之后选择一个区 间 [l,r] 使得 al +al+1 + ... +ar 最大。
【思路】
由于n比较小,我们考虑直接暴力贪心。
每次枚举最终的区间 [l,r]。 不断将 [l,r] 内最小的数换成外面最大的数。 即有 t≤k 个 [l,r] 内的数不算入,而有另外 t 个 [l,r] 外的 数算入。取最小的数和最大的数用优先队列实现即可。
时间复杂度O(n3k)
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
typedef long long ll;
const int maxn = 305;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
int n,m;
int a[maxn];
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
int ans=-INF;
for(int i=0;i<n;i++)
for(int j=i;j<n;j++)
{
priority_queue<int,vector<int>, less<int> > out;
priority_queue<int,vector<int>, greater<int> > in;
for(int k=0;k<n;k++)
{
if(k>=i&&k<=j) in.push(a[k]);
else out.push(a[k]);
}
int temp=m;
while(temp--)
{
if(in.size()==0||out.size()==0) break;
int u=in.top();
int v=out.top();
if(v<=u) break;
else
{
in.pop(),out.pop();
in.push(v),out.push(u);
}
}
int cnt=0;
while(in.size())
{
int u=in.top();
in.pop();
cnt+=u;
}
ans=max(ans,cnt);
}
printf("%d\n",ans);
}
}
上面的是naive版本,接下来我们考虑n≤ 10000的情况,如果还用上面的做法显然会超时
于是我们考虑用动态规划来解决这个问题。
我们知道,最后的答案肯定是一个区间,只是把区间内的一些数跟区间外的一些数进行了交换。
我们用dp[idx][st][out][in]来表示考虑到aidx,且状态为st,区间内out个数忽略,已经考虑了区间外in个数(即加进来的数的个数)的区间和最大值
对于st:
- st==0时表示区间未开始
- st==1时表示区间已经开始,但没有结束
- st==2时表示区间已经结束
然后记忆化搜索即可。
时间复杂度O(nk2)
#include <cstdio>
#include <cmath>
#include <set>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
typedef long long ll;
const int maxn = 10005;
const ll mod = 1e9+7;
const int INF = 1e9;
const double eps = 1e-6;
int n,k;
int a[maxn];
bool vis[maxn][3][15][15];
int dp[maxn][3][15][15];
int solve(int idx,int st,int out,int in)
{
if(idx==n)
{
if(in==out&&st) return 0;
return -INF;
}
if(vis[idx][st][out][in]) return dp[idx][st][out][in];
int cnt=-INF;
if(st==0) //区间未开始
{
cnt=max(solve(idx+1,1,out,in)+a[idx],solve(idx+1,0,out,in)); //区间开始考虑这个数&&区间仍未开始这个数不考虑
cnt=max(cnt,solve(idx+1,1,out+1,in)); //区间开始,这个数需要交换出去
if(in<k) cnt=max(cnt,solve(idx+1,0,out,in+1)+a[idx]); //区间未开始,这个数交换进去
}
else if(st==1) //区间开始,未结束
{
cnt=max(solve(idx+1,2,out,in),solve(idx+1,1,out,in)+a[idx]); //区间结束这个数不考虑&&区间未结束,考虑这个数
if(out<k) cnt=max(cnt,solve(idx+1,1,out+1,in)); //区间未结束,这个数交换出去
}
else //区间已经结束
{
cnt=max(cnt,solve(idx+1,2,out,in)); //这个数不考虑
if(in<k) cnt=max(cnt,solve(idx+1,2,out,in+1)+a[idx]); //这个数交换进去
}
vis[idx][st][out][in]=1;
return dp[idx][st][out][in]=cnt;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
mst(vis,0);
mst(dp,0);
printf("%d\n",solve(0,0,0,0));
}