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;
    }
    
  • 相关阅读:
    Linux Shell 基本语法
    VIM选择文本块/复制/粘贴
    linux vi命令详解2
    SSH命令详解2
    JAVA调用Shell脚本
    scp命令的用法详解
    Java实践 — SSH远程执行Shell脚本
    Remote SSH: Using JSCH with Expect4j
    c++内置函数---7
    c++将引用作为函数的参数---6
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15488525.html
Copyright © 2011-2022 走看看