Codeforces 426C - Sereja and Swaps 【优先队列||DP】



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:

  1. st==0时表示区间未开始
  2. st==1时表示区间已经开始,但没有结束
  3. 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));
}









  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值