将每门课等级拆成0,1,2,3...a[i]个点,对每一个等级大于0的点向它低一级连边,权值为0【意思是,若修了level k。则level(0~k)都当做修了】
将输入的边建边,权值为money[i]。
建立根节点,向每一个level 0的点连边,权值为0【由于初始level 0的都修了】
因为题目要求每门课都必须达到最大level,也就是相应图中根节点能到达全部点,问题就变成了求有向图的最小生成树。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm> using namespace std;
#define INF 0x3FFFFFF
#define MAXN 5555
struct edge
{
int u,v,w;
}e[9999999];
int n,en;
int pre[MAXN],in[MAXN],id[MAXN],vis[MAXN];
void add(int u,int v,int w)
{
e[en].u=u;
e[en].v=v;
e[en++].w=w;
}
int zl(int root ,int vn)
{
int ans=0;
int cnt;
while(1)
{
for(int i=0;i<vn;i++)
in[i]=INF,id[i]=-1,vis[i]=-1;
for(int i=0;i<en;i++)
{
if(in[e[i].v]>e[i].w && e[i].u!=e[i].v)
{
pre[e[i].v]=e[i].u;
in[e[i].v]=e[i].w;
}
}
in[root]=0;
pre[root]=root;
for(int i=0;i<vn;i++)
{
ans+=in[i];
if(in[i]==INF)
return -1;
}
cnt=0;
for(int i=0;i<vn;i++)
{
if(vis[i]==-1)
{
int t=i;
while(vis[t]==-1)
{
vis[t]=i;
t=pre[t];
}
if(vis[t]!=i || t==root) continue;
for(int j=pre[t];j!=t;j=pre[j])
id[j]=cnt;
id[t]=cnt++;
}
}
if(cnt==0) break;
for(int i=0;i<vn;i++)
if(id[i]==-1)
id[i]=cnt++;
for(int i=0;i<en;i++)
{
int u,v;
u=e[i].u;
v=e[i].v;
e[i].u=id[u];
e[i].v=id[v];
e[i].w-=in[v];
}
vn=cnt;
root=id[root];
}
return ans;
}
int a[MAXN],pres[MAXN];
int main()
{
int x,y,b,c,d,m;
while(~scanf("%d%d",&n,&m))
{
if(!n&&!m) break;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),
pres[i]=pres[i-1]+a[i]+1;
en=0;
int s=0;
int t=pres[n]+1;
for(int i=1;i<=n;i++)
{
for(int id=1;id<=a[i];id++)
{
add(pres[i-1]+id+1,pres[i-1]+id,0);
// printf("%d -> %d\n",pres[i-1]+id+1,pres[i-1]+id);
}
add(s,pres[i-1]+1,0);
// printf("%d -> %d\n",pres[i-1]+a[i]+1,t);
// printf("%d -> %d\n",s,pres[i-1]+1);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d%d",&x,&y,&b,&c,&d);
add(pres[x-1]+y+1,pres[b-1]+c+1,d);
// printf("%d -> %d\n",pres[x-1]+y+1,pres[b-1]+c+1);
}
int tmp=zl(0,t);
if(tmp<0) puts("-1");
else printf("%d\n",tmp);
}
return 0;
}