superoj921 编译优化

分析:

        此题为一个环,告诉你环上的权值,求最大选择和;

选择要求:选了A,与A相邻的俩个元素就不能选。

数据 60% n=5000

       100% n=200000

我65分dp

60% dp

       dp[i][j]=max(dp[i-1][j],dp[i-2][j-1]+a[i]);

       再多旋转几圈防止1和n冲突           复杂度O(N^2)

代码:

#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
//#include<ctime>
using namespace std;
int n,m;
int a[5010];
int b[5010];
int dp[5010][5010];
int ans;
int read()
{
	int k=0,f=1;
    char c=getchar();
	while(c>'9'||c<'0') {if(c=='-') f=-1; c=getchar();}
	while(c>='0'&&c<='9') {k=k*10+(c-'0'); c=getchar();}
	return k*f;
}

int main()
{
   freopen("compile.in","r",stdin);
   freopen("compile.out","w",stdout);
   int i,j,k;
   n=read();
   m=read();
   if(m>(n>>1)) {cout<<"Error!";return 0;}
   for(i=1;i<=n;i++)
     {
     	a[i]=read();
	 }
   
   for(i=1;i<=n;i++)
     for(j=1;j<=m;j++) dp[i][j]=-1000000000;
   
   dp[1][1]=a[1];
   dp[2][1]=max(a[1],a[2]);
   
   	 
   for(i=3;i<=n;i++)
     for(j=1;j<=min(m,(n+1)>>1);j++)
       {
       	  dp[i][j]=max(dp[i-2][j-1]+a[i],dp[i-1][j]);
	   }
   ans=dp[n][m];
   
   k=0;
   for(i=n/3;i<=n;i++)  
     	b[++k]=a[i];
	 
   for(i=1;i<n/3;i++)
        b[++k]=a[i];
   for(i=1;i<=n;i++) a[i]=b[i];     
   
   for(i=1;i<=n;i++)
     for(j=1;j<=m;j++) dp[i][j]=-1000000000;
     
   dp[1][1]=a[1];
   dp[2][1]=max(a[1],a[2]);
   
   	 
   for(i=3;i<=n;i++)
     for(j=1;j<=min(m,(n+1)>>1);j++)
       {
       	  dp[i][j]=max(dp[i-2][j-1]+a[i],dp[i-1][j]);
	   }
   ans=min(dp[n][m],ans);
   
   k=0;
   for(i=n/2+1;i<=n;i++)  
     	b[++k]=a[i];
	 
   for(i=1;i<=n/2;i++)
        b[++k]=a[i];
   for(i=1;i<=n;i++) a[i]=b[i];     
   
   for(i=1;i<=n;i++)
     for(j=1;j<=m;j++) dp[i][j]=-1000000000;
     
   dp[1][1]=a[1];
   dp[2][1]=max(a[1],a[2]);
   
   	 
   for(i=3;i<=n;i++)
     for(j=1;j<=min(m,(n+1)>>1);j++)
       {
       	  dp[i][j]=max(dp[i-2][j-1]+a[i],dp[i-1][j]);
	   }
   ans=min(dp[n][m],ans);   
   
   cout<<ans;     
   return 0;
}


100%数据

       利用网络流残余流量思想(并不懂)

       建立大根堆,维护权值和映射

       用了B,就废掉A,C,再加入A+C-B

 若下一次选了A+C-B 则总和为A+C

       实现过程用的queue

       因为要修改左右元素是什么,而queue里面没有编号无法修改queue内的内容(例如迪杰斯特拉的手写堆是因为修改dis为queue内排序基准),

所以要用映射。

       每个queue记录权值和映射,映射表示它在queue外普通int数组的编号,修改时直接修改int数组的值,queue内元素映射出来自然就是修改后的值了

左右元素用l,r储存(like dancing link)l[i]=l[l[i]],r[l[i]]=i...........     ban判断是否废掉了(删点方法2333)

AC代码:

//queue内开映射 
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <cstdlib>
#include <cstdio>
#include <map>
#include <cstring>
#include <queue>
#include <string>
#define N 200100
#define INF (int)1e9
using namespace std;
struct node 
{
    int v;
    int p;     //映射 
    bool  operator < (const node &a)const
    {
        return v<a.v;
    }
};

priority_queue <node> q;

int n;
int m;
int a[N];
int l[N];
int r[N];
bool ban[N];
int ans;

int main ()
{
    //freopen("cs.in","r",stdin);
    //freopen("cs.out","w",stdout);
    scanf("%d%d",&n,&m);
    if(m>n/2)
    {
        printf("Error!\n");
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        l[i]=i-1;r[i]=i+1;
        node t;
		t.v=a[i];
		t.p=i; 
        q.push(t);
    }
    l[1]=n;r[n]=1;
    for(int i=1;i<=m;i++)
    {
        while(ban[q.top().p])q.pop();   
        int w=q.top().v;
        int x=q.top().p;
        q.pop();
        ans+=w;
        a[x]=a[l[x]]+a[r[x]]-a[x];
        ban[l[x]]=ban[r[x]]=true;
        l[x]=l[l[x]];r[x]=r[r[x]];
        r[l[x]]=x;l[r[x]]=x;
        node t;
		t.v=a[x];
		t.p=x; 
        q.push(t);
    }
    cout<<ans;
    return 0;
    
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值