• P4480 「BJWC2018」「网络流与线性规划24题」餐巾计划问题


    刷了n次用了奇淫技巧才拿到rk1,亥

    这道题是网络流二十四题中「餐巾计划问题」的加强版。

    于是怀着试一试的心情用费用流交了一发:

    哇塞,过了9个点!(强烈谴责出题人用*造数据

    下面是费用流解法简述:

    那么我们把每一天拆为早上和晚上两个点,设为 (day_i,night_i)

    首先可以人为地规定一点:对一块餐巾,我们要么在其脏了之后立即送洗,要么买一块新干净餐巾来代替它。

    然后对于每一个操作,我们可以如下连边:

    对于买新干净餐巾:我们可以视作从源点买餐巾,从 (s)(day_i) 连边即可

    对于送慢洗部:从 (night_i)(day_{i+n}) 连边,流量为 (inf) ,费用为 (f)

    对于送快洗部:从 (night_i)(day_{i+m}) 连边,流量为 (inf) ,费用为 (s)

    但是我们注意到,可能存在某一天的干净餐巾冗余。

    于是我们要从 (day_i)(day_{i+1}) 连一条费用为零,流量为 (inf) 的单向边。

    如何保证每天刚好只用 (r_i) 块餐巾?

    将其拆成两个板块:

    (day_i)(t) 连边,流量为 (r_i),费用为 (0)

    (s)(night_i) 连边,流量为 (r_i),费用为 (0)


    优化无果,于是猜想可以不用费用流。

    首先无论餐巾是最开始一起买还是需要用时再买,不会影响最终的答案。

    那么如果我们已经确定了要买的新餐巾的张数,那么是否可以确定一种唯一的方案使得总花费最小呢?

    不难得到有这样一种贪心策略:

    在能够用新餐巾的时候,尽量使用新餐巾。

    (m) 为慢洗的天数。

    如果无新餐巾可用,则倒回到 (m) 天前,找用过的旧餐巾进行慢洗。

    如果没有 (m) 天以前的旧餐巾可用,则由时间线从近到远地找旧餐巾进行快洗。

    这样做使得时间线较远的旧餐巾更有可能慢洗。

    如果慢洗比快洗贵,那么直接将慢洗的时间和价格都改为快洗的时间和价格即可。

    朴素代码无O2只能过掉70分。

    于是我怀疑单次判断的时间复杂度过高。有的题解里说是 (O(n)) 的,但我死活没看出来。

    于是我们可以优化常数。

    注意到在如果在第 (i) 天需要慢洗,所有在第 (i-m) 天前的旧餐巾对于我们来说是等效的,因为我们显然没有办法将其拿去快洗。

    所以我们每次将第 (i-m) 天前的旧餐巾全部累加至第 (i-m) 天即可。这样可以优化一定的常数。

    这样可以保证在计算慢洗的时候一定是线性的,但仍然不能保证计算快洗部分时为线性。

    最后我们需要确定最优解时需购买餐巾的张数 (c)

    设最优解时最小代价为 (f(c)),设当前枚举到了 (x)

    则当 $x ge c $,一定有 (f(x) ge f(c)),因为你还需要多出钱买新餐巾。

    (x le c),则要么不存在 (f(x)) ,即购买 (x) 张餐巾不能达到目的,要么此时多洗一张餐巾一定劣于多买一张餐巾,也有 (f(x) ge f(c))

    所以对于最优解 (c) 我们可以三分求解。

    个人认为代码可读性还是挺高的,可以康康代码:

    /*---Author:HenryHuang---*/
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll maxn=5e5+5;
    const ll inf=1ll<<50;
    ll sumr=0;
    ll r[maxn],res[maxn];
    ll n,m1,m2,c1,c2,p;
    ll f(ll num){
    	ll ans=1ll*num*p;//提前算好新餐巾代价 
    	ll now=0;
    	for(ll i=1;i<=n;++i){
    		res[i]=0;
    		if(r[i]){
    			ll tmp=r[i];
    			if(num){
    				ll k=min(tmp,num);
    				tmp-=k,res[i]+=k,num-=k;
    				if(!tmp) continue;
    			}//直接用新的 
    			for(ll j=now;j<i-m2;++j){
    				res[j+1]+=res[j],res[j]=0;
    			}//累加旧洗餐巾 
    			now=max(now,i-m2);
    			if(now<i&&res[now]){
    				ll k=min(tmp,res[now]);
    				res[now]-=k,res[i]+=k;
    				tmp-=k,ans+=1ll*k*c2;
    				if(!tmp) continue;
    			}//慢洗 
    			for(ll j=i-m1;j>=1&&j>i-m2;--j){
    				if(res[j]){
    					ll k=min(tmp,res[j]);
    					res[j]-=k,res[i]+=k;
    					tmp-=k,ans+=1ll*k*c1;
    				}
    				if(!tmp) break;
    			}//快洗 
    			if(tmp) return inf;
    		}
    	}
    	return ans;
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	cin>>n>>m1>>m2>>c1>>c2>>p;
    	if(m1>m2) swap(m1,m2),swap(c1,c2);//1快2慢
    	if(c1<c2) c2=c1,m2=m1; 
    	for(ll i=1;i<=n;++i)
    		cin>>r[i],sumr+=r[i];
    	ll l=1,r=max(10000ll,sumr/3);//奇淫技巧 
    	ll ans=inf;
    	while(r-l>2){
    		ll k=(r-l)/3;
    		ll mid1=l+k,mid2=r-k;
    		ll aa=f(mid1),bb=f(mid2);
    		if(aa<bb) ans=min(ans,aa),r=mid2;
    		else ans=min(ans,bb),l=mid1;
    	}
    	for(ll i=l;i<=r;++i) ans=min(ans,f(i));
    	cout<<ans<<'
    ';
    	return 0;
    }
    
  • 相关阅读:
    Truffle 安装
    Windows10 修改代理
    windows远程桌面(mstsc)不能复制粘贴的解决办法(转)
    响应式布局 笔记汇总
    Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'hive.DELETEME1643159643943' doesn't exist
    Oracle安装问题:Win10系统离线安装framework3.5报0x8024402c的问题
    NameNode启动问题:Failed to load an FSImage file!
    他们的名言警句&推荐书籍
    没有可以使用的 internal type for both IPv4 and IPv6 Addresses (A+AAAA)记录
    【转】ExtJs 正则
  • 原文地址:https://www.cnblogs.com/HenryHuang-Never-Settle/p/solution-P4480.html
Copyright © 2020-2023  润新知