题面:
https://www.luogu.org/problemnew/show/P1251
思路:
这道题乍一看,可以跑上下界费用流
代码量、难度 -> inf
其实不然,我们可以用费用流的特殊处理去掉下界
观察题目,每天要求有ri块餐巾
首先,有贪心如下:
当且仅当每天可供使用的餐巾正好满足需求时,可以有最小费用
证明:若某一天有多一块餐巾,则其根本来源一定是买多了,而且在这块餐巾参与的周转中还消费了一些清洗费用,同时它造成其余的日子里也会有餐巾被闲置
因此首先把题目转化为“每天正好使用ri”
此时考虑拆点,将每一天拆成xi,yi,其中yi连边到T,容量为ri,费用为0,表示该天需要这么多餐巾。(以下这类边表示为 (cap,cost) )
然后开始建图,思路是保证yi到T的边可以满流的情况下,构建费用等同于题目要求的网络。
首先定义xi为“第i天对于用完的餐巾做出的决策”,因此可以从xi到xi+1连一条边 (inf,0),其边上的流量表示“第i+1天保留第i天中(流量)块餐巾的决策”
然后从S到xi连边 (ri,0) ,代表该天需要做出这一决策的餐巾数量,也就是该天使用了的餐巾数量
从S到yi连边(inf,p)代表新买一块。这块餐巾不需要参与xi的“旧餐巾决策”,直接连到yi
从xi到yi+m、yi+n分别连边 (inf,f) (inf,s) ,代表快洗和慢洗决策。
然后在这张图上跑S-T最小费用最大流即可
Code:
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define inf 100000000000000LL #define ll long long using namespace std; inline ll read(){ ll re=0,flag=1;char ch=getchar(); while(ch>‘9‘||ch<‘0‘){ if(ch==‘-‘) flag=-1; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘) re=(re<<1)+(re<<3)+ch-‘0‘,ch=getchar(); return re; } ll n,m,cnt,ans,flow,first[100010]; ll dis[100010],pre[100010],path[100010]; struct edge{ ll to,next,w,cap; }a[100010]; inline void add(ll u,ll v,ll w,ll cap){ // cout<<"add "<<u<<ends<<v<<ends<<w<<ends<<cap<<endl; a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt; a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt; } void init(){ memset(first,-1,sizeof(first)); memset(a,0,sizeof(a));cnt=-1;ans=0;flow=0; } ll spfa(ll s,ll t){ ll q[10010],head=0,tail=1,maxn=10000,i,u,v,w; memset(pre,-1,sizeof(pre)); for(i=s;i<=t;i++) dis[i]=inf; q[0]=s;dis[s]=0; while(head!=tail){ u=q[head];head=(head+1)%maxn; // cout<<"spfa "<<u<<endl; for(i=first[u];~i;i=a[i].next){ v=a[i].to;w=a[i].w; // cout<<" to "<<v<<ends<<dis[v]<<ends<<w+dis[u]<<endl; if(a[i].cap&&dis[v]>dis[u]+w){ dis[v]=dis[u]+w; pre[v]=u;path[v]=i; q[tail]=v;tail=(tail+1)%maxn; } } } return ~pre[t]; } inline ll _min(ll l,ll r){return (l<r)?l:r;} ll mcmf(ll s,ll t){ ll f,u; while(spfa(s,t)){ f=inf; for(u=t;u!=s;u=pre[u]) f=_min(f,a[path[u]].cap); flow+=f;ans+=dis[t]*f; // cout<<"mcmf "<<flow<<ends<<ans<<endl; for(u=t;u!=s;u=pre[u]){ a[path[u]].cap-=f; a[path[u]^1].cap+=f; } } } int main(){ // freopen("napkin.in","r",stdin); // freopen("napkin.out","w",stdout); init(); ll i,t1,t2; n=read(); for(i=1;i<=n;i++){ t1=read(); add(0,i,0,t1); add(i+n,(n<<1)+1,0,t1); } for(i=1;i<n;i++) add(i,i+1,0,inf); t1=read(); for(i=1;i<=n;i++) add(0,i+n,t1,inf); t1=read();t2=read(); for(i=1;i<=n-t1;i++) add(i,i+t1+n,t2,inf); t1=read();t2=read(); for(i=1;i<=n-t1;i++) add(i,i+t1+n,t2,inf); mcmf(0,(n<<1)+1); printf("%lld",ans); }