zoukankan      html  css  js  c++  java
  • CF710E Generate a String

    题面

    solution

    有很多做法的一道题。

    根据题意,一个很显然的 (dp)

    (f_{i}) 表示生成长度为 (i) 的序列的最小代价。

    转移:

    (i) 是奇数:

    (f_{i} = min{f_{i - 1} + x, f_{i + 1} + x})

    (i) 是偶数:

    (f_{i} = min{f_{i - 1} + x, f_{i + 1} + x, f_{i / 2} + x})

    solution1

    你会发现这个 (dp) 转移成环,所以没法转移。

    考虑把状态看作点,转移看作状态之间连边,跑最短路。

    题解代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<queue>
    #define mp make_pair
    using namespace std;
    typedef long long LL;
    
    const int maxn=20000005;//可能会先超过再删到N.
    LL dis[maxn];
    int N,x,y,tot;
    bool vis[maxn];
    priority_queue<pair<LL,int> > Q;
    
    LL Dij()
    {
    	memset(dis,0x3f,sizeof(dis));
    	dis[1]=x;
    	Q.push(mp(-dis[1],1));
    	while(!Q.empty())
    	{
    		int q=Q.top().second;Q.pop(),vis[q]=true;
    		if(q==N) return dis[N];
    		if(q>1&&!vis[q-1]&&dis[q-1]>dis[q]+x) Q.push(mp(-(dis[q-1]=dis[q]+x),q-1));
    		if(q<N&&!vis[q+1]&&dis[q+1]>dis[q]+x) Q.push(mp(-(dis[q+1]=dis[q]+x),q+1));
    		if(q<N&&!vis[q<<1]&&dis[q<<1]>dis[q]+y) Q.push(mp(-(dis[q<<1]=dis[q]+y),q<<1));
    		while(!Q.empty()&&vis[Q.top().second]) Q.pop();
    	}
    	return dis[N];
    }
    
    int main()
    {
    	scanf("%d%d%d",&N,&x,&y);
    	printf("%I64d",Dij());
    	return 0;
    }
    

    solution2

    分析一下会发现,删除操作只有在进行翻倍操作之后,翻超了才会进行删除操作。

    然后就可以改一下转移方程了:

    (f_{i} = min{f_{i - 1} + x, f_{k} + (2 imes k - i) imes x + y})

    其中 (2 imes k > i && k < i)

    把上面的柿子展开

    [f_{i} = min{f_{i - 1} + x, f_k + 2kx - ix + y} ]

    你会发现 (f_k + 2kx) 这一项是只和 (k) 有关的柿子,这个用单调队列维护就好了。

    code

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int MAXN = 1e7 + 5;
    int read() {
      int x = 0, f = 1; char c = getchar();
      while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
      while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
      return x * f;
    }
    int n, x, y, f[MAXN], head = 1, tail, q[MAXN];
    void Delet(int i) {
       while(head <= tail && q[head] * 2 < i) head++;
    }
    void Insert(int i) {
      while(head <= tail && f[q[tail]] + 2ll * q[tail] * x >= f[i] + 2ll * i * x) tail--;
      q[++tail] = i;
    } 
    signed main() {
       n = read(), x = read(), y = read();
       f[0] = 0; 
       for (int i = 1; i <= n; i++) {
       	  Delet(i);
       	  f[i] = f[i - 1] + x; 
       	  if(head <= tail) f[i] = min(f[q[head]] + (2ll * q[head] - i) * x + y, f[i]);
       	  Insert(i);
       }
       cout<<f[n];
       return 0;	
    }
    

    solution3

    你再仔细分析一下。

    每个删除操作不会连续进行两次。

    因为进行两次删除操作不如在翻倍前进行一次删除操作更优。

    所以当 (i) 是偶数的话不会由删除操作转移:

    (f_i = min{f_{i - 1} + x, f_{i / 2} + y})

    (i) 是奇数的话:

    (f_i = min{f_{i - 1} + x, f_{(i + 1) / 2} + y + x})

    code

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int MAXN = 1e7 + 5;
    int read() {
      int x = 0, f = 1; char c = getchar();
      while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
      while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
      return x * f;
    }
    int n, x, y, f[MAXN];
    signed main(){
      n = read(), x = read(), y = read(); 
      f[0] = 0;
      for (int i = 1; i <= n; i++) {
      	f[i] = f[i - 1] + x;
      	if(!(i & 1)) f[i] = min(f[i / 2] + y, f[i]);
      	else f[i] = min(f[(i + 1) / 2] + x + y, f[i]);
      }
      cout<<f[n];
      return 0;
    }
    
  • 相关阅读:
    element ui 表单清空
    element ui 覆盖样式 方法
    element ui 修改表单值 提交无效
    element ui 抽屉里的表单输入框无法修改值
    element ui 抽屉首次显示 闪烁
    css 左侧高度 跟随右侧内容高度 自适应
    PICNUF框架
    elementui 抽屉组件标题 出现黑色边框
    vue 子组件跨多层调用父组件中方法
    vue 编辑table 数据 未点击提交,table里的数据就发生了改变(深拷贝处理)
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15488525.html
Copyright © 2011-2022 走看看