费用流加动态建点。
考虑加的点是倒数第几个做的菜,它对答案的共享就是i倍的时间。
和bzoj1070修车那道题一样。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxk = 100 + 10; const int maxn = 10000 + 10; const int maxm = 2000000 + 10; const int inf = 0x3f3f3f3f; int g[maxn],v[maxm],next[maxm],c[maxm],f[maxm],eid; int n,m,res,p,vid,S,T; int id[maxk][1010],cnt[maxk]; int a[maxk][maxk]; int q[maxm],l,r,u; bool inque[maxn]; int dist[maxn],pre[maxn]; void addedge(int a,int b,int F,int C) { v[eid]=b; f[eid]=F; c[eid]=C; next[eid]=g[a]; g[a]=eid++; v[eid]=a; f[eid]=0; c[eid]=-C; next[eid]=g[b]; g[b]=eid++; } void build() { memset(g,-1,sizeof(g)); scanf("%d%d",&n,&m); vid=n; S=0; T=maxn-10; for(int i=1,x;i<=n;i++) { scanf("%d",&x); addedge(S,i,x,0); p+=x; } for(int i=1;i<=m;i++) { id[i][++cnt[i]]=++vid; addedge(id[i][cnt[i]],T,1,0); } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&a[i][j]); addedge(i,id[j][cnt[j]],1,a[i][j]); } } void augment() { int aug=inf,cur=0; for(int i=T;i!=S;i=v[pre[i]^1]) aug=min(aug,f[pre[i]]); for(int i=T;i!=S;i=v[pre[i]^1]) { f[pre[i]]-=aug; f[pre[i]^1]+=aug; cur+=aug*c[pre[i]]; } res+=cur; } int SPFA() { memset(dist,0x3f,sizeof(dist)); dist[S]=0; q[r++]=S; while(l<r) { inque[u=q[l++]]=0; for(int i=g[u];~i;i=next[i]) if(f[i]&&dist[v[i]]>dist[u]+c[i]) { dist[v[i]]=dist[u]+c[i]; pre[v[i]]=i; if(!inque[v[i]]) inque[q[r++]=v[i]]=1; } } //printf("test %d\n",dist[T]); augment(); return v[pre[T]^1]; } void alter(int pos) { int x; for(int i=1;i<=m;i++) if(id[i][cnt[i]]==pos) {x=i; break;} id[x][++cnt[x]]=++vid; addedge(id[x][cnt[x]],T,1,0); for(int i=1;i<=n;i++) addedge(i,id[x][cnt[x]],1,a[i][x]*cnt[x]); } void solve() { res=0; for(int i=1;i<=p;i++) alter(SPFA()); printf("%d\n",res); } int main() { build(); solve(); return 0; }
时间: 2024-10-06 23:44:08