zoukankan      html  css  js  c++  java
  • 【[USACO13NOV]没有找零No Change】

    其实我是点单调队列的标签进来的,之后看着题就懵逼了

    于是就去题解里一翻,发现楼上楼下的题解说的都好有道理,
    f[j]表示一个再使用一个硬币就能到达i的某个之前状态,b[now]表示使用那个能使状态j变到i的硬币的面值,find表示这些花费可以到达的最大距离,由于前缀和保持单调可以用二分求解,方程不就是f[i]=max(f[i],find(p[f[j]]+b[now]))吗

    但这道题怎么用单调队列优化呢

    我们观察这个方程你会发现无论是b[now],p[f[j]]都跟i没有什么关系,而只要是p[f[j]]+b[now]越大,相应的find的值也就越大

    于是我们就可以愉快的单调队列优化这个dp了,用一个单调队列把每次的p[f[j]]+b[now]存起来,每次有新元素入队时维护队列的单调性,之后当所有元素入队完,直接用队首的最大值进行一遍find就好了,这样就可以避免进行多次find了

    尽管单调队列是用常数奇大的deque实现的,但开了O2能跑到120 ms,轻松卡到最优解第一

    #include<iostream>
    #include<queue>
    #include<cstring>
    #include<cstdio>
    #define re register
    #define int long long
    #define maxn 100001
    using namespace std;
    int f[65540],a[maxn],b[17],n,m,num,p[maxn];
    int c[17];
    inline void check(int x)
    {
        memset(c,0,sizeof(c));
        int pp=m;
        while(x)
        {
            if(x&1) c[pp]=1;
            pp--;
            x>>=1;
        }
    }
    inline int read()
    {
        char c=getchar();
        int x=0;
        while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9')
          x=(x<<3)+(x<<1)+c-48,c=getchar();
        return x;
    }
    inline int find(int x)
    {
        int l=1,r=n,tot=0;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(p[mid]<=x) l=mid+1,tot=mid;
            else r=mid-1;
        }
        return tot;
    }
    signed main()
    {
        m=read();
        n=read();
        for(re int i=1;i<=m;i++) b[i]=read(),num+=b[i];
        for(re int i=1;i<=n;i++) a[i]=read();
        p[1]=a[1];
        for(re int i=2;i<=n;i++) p[i]=p[i-1]+a[i];
        for(re int i=0;i<=(1<<m)-1;i++)
        {
        	deque<int> q;
        	for(re int j=1;j<=m;j++)
        	{
            	if(!(i&(1<<(m-j)))) continue;//这里跟楼上楼下几篇题解不太一样,这个状态转成二进制后左边第一位表示的是第一个硬币是否被选择
            	int k=i^(1<<(m-j));
            	while(q.size()&&q.back()<p[f[k]]+b[j]) q.pop_back();//跟队尾元素比较,如果比队尾大就弹出队尾,维护单调队列单调性
            	q.push_back(p[f[k]]+b[j]);//入队
     		}
            //其实这里不用单调队列优化也是可以的,我们只需要存储一下最大值就好了,这样应该还能快一些,但是用单调队列优化dp的这种思路还是比较重要的
     		f[i]=find(q.front());//只用一遍find就好了
        }
     	int ans=-1;
     	for(re int i=0;i<=(1<<m)-1;i++)
     	{
     		if(f[i]!=n) continue;//当前状态根本到不了n,就直接下一个
     		check(i);//将当前的状态转成二进制数
     		int tot=0;
     		for(re int j=1;j<=m;j++)
     		if(c[j]==0) tot+=b[j];
            //如果没有被选择,那么就把它加上
     		if(tot>ans) ans=tot; 
        }
        cout<<ans;
        return 0;
    }
    
  • 相关阅读:
    VS2008 环境中完美搭建 Qt 4.7.4 静态编译的调试与发布 Inchroy's Blog 博客频道 CSDN.NET
    编写可丢弃的代码
    c++ using namespace std; 海明威 博客园
    解决MySQL server has gone away
    nginx upstream 调度策略
    (2006, 'MySQL server has gone away') 错误解决 dba007的空间 51CTO技术博客
    Linux IO模型漫谈(2) 轩脉刃 博客园
    redis源码笔记 initServer 刘浩de技术博客 博客园
    MySQLdb批量插入数据
    词库的扩充百度百科的抓取你知道这些热词吗? rabbit9898 ITeye技术网站
  • 原文地址:https://www.cnblogs.com/asuldb/p/10207855.html
Copyright © 2011-2022 走看看