题目(原文是日语):
Google Code Jam区域赛上,坐在右前方的男人ID叫lyrically。东京大学时代的记忆中,记得有个朋友也用类似的ID。不过我的朋友都是萌妹子,我记忆中的 lyrically不仅算法扎实,封装也很强,能一下子给出问题的正解。比如,对我们写得不好的程序也能优化到AC的程度。她说,程序优化时,对缓存池的 利用特别重要。
那么问题来了,现在请你优化下面的缓存池模型:
有M个桶,N个球,球编号为1到N,每个球都有重量w_i。然后给出一个长K的数列,数列由球的编号构成。开始的时候,桶都是空的。接着我们从前往后从数列中取出一个数a_j,执行如下操作:
如果球a_j已经存在于某一个桶中,那么什么也不干,花费为0,继续。
如果任何桶中都没有a_j,那么找一个桶放a_j,如果该桶里还有球,那么取出原来的球,将a_j放进去。这种操作的花费是a_j的重量w_a_j,与桶以及原来的球没关系。
求最小花费?
题目数据第一行是M N K,接着N行权重,K行编号。
思路:首先这是《挑战程序设计》的费用流部分的练习题。。那么就要从费用流的方向来考虑。我们使用桶子的时候,每个时候一定有一个桶子是要用来装新球的,也就是说这个桶子不是保留的桶子,同时其他桶子可以是空桶,也可以装了东西。之后可能会用到之前的球来减少花费。那么我们只需要考虑最有效的保留方式能节省多少钱。同时最多有m-1个桶是保留的,保留多久呢?保留到下一次遇到相同的球。那么如果选择保留这个桶,这个桶在这个区间内都是不可用的。仔细想想,这不就是书上的例题最大权区间选择吗!(poj 3680).那么问题就是,如何选择最多m-1个相交区间,使得权值和最大。
题目翻译来自:http://www.hankcs.com/program/algorithm/aoj-2266-cache-strategy.html
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; typedef pair<ll,int> D; const int MAXV=1e4+300; int V; const int s=MAXV-1,t=MAXV-2; struct EDGE{ int to,cap,cost,next; }ES[MAXV*10]; int eh; int h[MAXV]; int dis[MAXV]; int prevv[MAXV],preve[MAXV]; int head[MAXV]; void addedge(int from,int to,int cap,int cost){ ES[eh].to=to;ES[eh].cap=cap;ES[eh].cost=cost; ES[eh].next=head[from];head[from]=eh++; ES[eh].to=from;ES[eh].cap=0;ES[eh].cost=-cost; ES[eh].next=head[to];head[to]=eh++; } bool inq[MAXV]; ll min_cost_flow(int s,int t,int f){ V=MAXV;//default V size maxed ll res=0; memset(h,0,sizeof h); queue<P> Q;////spfa计算势h fill(dis,dis+V,INF); dis[s]=0; Q.push(P(0,s)); inq[s]=1; while(!Q.empty()){ P p=Q.front();Q.pop(); int v=p.se; inq[v]=0; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ dis[e.to]=dis[v]+e.cost +h[v]-h[e.to]; prevv[e.to]=v; preve[e.to]=i; if(!inq[e.to]) Q.push(P(dis[e.to],e.to)),inq[e.to]=1; } } } for(int v=0;v<V;v++) h[v]+=dis[v]; while(f>0){ priority_queue<P,vector<P> ,greater<P> >Q;////Dijkstra计算势h fill(dis,dis+V,INF); dis[s]=0; Q.push(P(0,s)); while(!Q.empty()){ P p=Q.top();Q.pop(); int v=p.se; if(dis[v]<p.fs) continue; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ dis[e.to]=dis[v]+e.cost +h[v]-h[e.to]; prevv[e.to]=v; preve[e.to]=i; Q.push(P(dis[e.to],e.to)); } } } if(dis[t]==INF) return -1; for(int v=0;v<V;v++) h[v]+=dis[v]; int d=f; for(int v=t;v!=s;v=prevv[v]) d=min(d,ES[preve[v]].cap); f-=d; res+=d*h[t]; for(int v=t;v!=s;v=prevv[v]){ EDGE &e=ES[preve[v]]; e.cap-=d; ES[preve[v]^1].cap+=d; } } return res; } void clear_G(){ eh=0; memset(head,-1,sizeof head); } const int maxn=1e4+300; int M,N,K; int a[maxn],w[maxn]; int last[maxn]; int tol; int hh; void build(){ for(int i=1;i<hh;i++){ tol+=w[a[i]]; if(last[a[i]]) addedge(last[a[i]],i-1,1,-w[a[i]]); last[a[i]]=i; } for(int i=1;i<hh-1;i++) addedge(i,i+1,INF,0); } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); //freopen("/home/slyfc/CppFiles/out","w",stdout); clear_G(); cin>>M>>N>>K; for(int i=1;i<=N;i++) scanf("%d",&w[i]); for(int i=1;i<=K;i++) scanf("%d",&a[i]); hh=unique(a+1,a+K+1)-a; build(); int ans=min_cost_flow(1,hh-1,M-1); cout<<tol+ans<<endl; return 0; }