有一个人,最开始所有技能等级都是0。有m种药,对于每种药,如果他的c技能等级达到了l1,就可以用e的费用嗑药,使d技能的等级达到l2。当然如果d技能的等级已经超过了l2,那他的技能等级也不会下降,而是会维持原来的等级。
问把所有技能等级都提升到满级,最少需要多少花费。
所以每个技能从高等级向低等级连边,初始点向所有的0技能连边,这些边的费用都是0。然后对于每种药,从c技能的l1等级向d技能的l2等级连边,费用为该药的费用。求最小树形图就行。
最小树形图就是有向图的最小生成树,在给定一个点作为根的前提下,要求所有的边都是从根向叶子方向连的。用一种叫朱刘算法的方法做..复杂度为n*m..据说很暴力...
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
int map[600][600];
using namespace std;
int n,m;
int TOT;
int INF = 0x1f1f1f1f;
void addedge(int a ,int b,int c)
{
//printf("%d %d %d\n",a,b,c);
map[a][b]=min(map[a][b],c);
}
int flag[600],father[600];
void merg(int k,int r)
{
for(int i=0;i<n;i++)
{
map[k][i]=min(map[k][i],map[r][i]);
map[i][k]=min(map[i][k],map[i][r]);
}
for(int i=0;i<n;i++)map[r][i]=map[i][r]=INF;
}
bool dfs(int k,int r)
{
flag[k]=r;
int len=INF;
int t=-1;
for(int i=0;i<n;i++)
{
if(len>map[i][k])
len=map[i][k],t=i;
}
if(t==-1)return false;
TOT+=len;
father[k]=t;
if(flag[t]!=-1 && flag[t]<r)return true;
else if(flag[t]==r)
{
int d=t;
do
{
for(int i=0;i<n;i++)
{
if(i==father[d])continue;
if(map[i][d]!=INF)map[i][d]-=map[father[d]][d];
}
d=father[d];
}while(d!=t);
d=t;
do
{
t=father[t];
merg(d,t);
}while(t!=k);
map[d][d]=INF;
return dfs(d,r);
}
else return dfs(t,r);
}
bool exist(int root)
{
memset(flag,-1,sizeof(flag));
flag[0]=0;
for(int i=1;i<n;i++)
{
if(flag[i]!=-1)continue;
if(!dfs(i,i))return false;
}
return true;
}
int init(int n,int m) {
static int a[50];
static int st[51];
//addedge(a,b,c);
int newn=0,i,j;
st[0]=1;
for (i=0;i<n;i++) {
scanf("%d",&a[i]);
st[i+1]=a[i]+1+st[i];
addedge(0,st[i],0);
for (j=0;j<a[i];j++) {
addedge(st[i]+j+1,st[i]+j,0);
}
}
newn=st[n];
for (i=0;i<m;i++) {
int c,l1,d,l2,e;
scanf("%d%d%d%d%d",&c,&l1,&d,&l2,&e);
addedge(st[c-1]+l1,st[d-1]+l2,e);
}
return newn;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF )
{
if(n==0 && m==0)break;
memset(map,0x1f,sizeof(map));
n=init(n,m);
TOT=0;
if(exist(0))printf("%d\n",TOT);
else printf("-1\n");
}
return 0;
}