http://acm.hdu.edu.cn/showproblem.php?pid=4966
3 4 3 3 1 1 0 2 3 10 2 1 1 2 10 1 2 3 1 10 3 1 1 3 10 0 0
40
题目大意:有n个课程,每个课程又等级a[i],初始都在等级0,满足c[j]课程等级大于等于L1[j]时,可花费money[j]使得d[j]课程等级达到L2[j],求最少需要多少钱?
把每个课程点每一个等级看成一个点,则题目给点条件就可以看成有向边,再添上每一个课程高等级到低等级点权值为0的边,就可以使c[j]课程等级大于L1[j]时,仍可花费money[j]达到d[j]课程等级L2[j],再添加一个虚拟点根结点,使其能无花费到达每一个课程的0等级结点。
这样就可以直接用 最小树形图 点模板了。
最开始一直WA,最后把MAXN从505换成555就AC了,然后就反应过来最大算错了(不提示越界很是费解) : (
#include <cstdio> #include <cstring> using namespace std; const int MAXN=555; const int MAXM=2555; const int INF=0x3f3f3f3f; int n,m; int pre[MAXN],id[MAXN],vis[MAXN];//pre[i]表示以i点为终点的最小权值入边的起点,id[i]表示i点在新图上对应的点 int inEdge[MAXN];//inEdge[i]表示以i点为终点的入边的最小权值 struct Edge { int s,e; int w; }edge[MAXM]; int Directed_MST(int root) { int s,e; int ans=0; while(true) { for(int i=0;i<n;++i) inEdge[i]=INF; for(int i=0;i<m;++i) {//找权值最小的入边 s=edge[i].s; e=edge[i].e; if(edge[i].w<inEdge[e]&&s!=e) {//如果当前边非自环且权值更小 pre[e]=s; inEdge[e]=edge[i].w; } } for(int i=0;i<n;++i)//如果存在一点没有入边则无法形成最小树形图 if(i!=root&&inEdge[i]==INF) return -1; int cnt=0;//cnt表示新图的点数 memset(id,-1,sizeof(id)); memset(vis,-1,sizeof(vis)); inEdge[root]=0; for(int i=0;i<n;++i) { ans+=inEdge[i]; int cur=i; while(vis[cur]!=i&&cur!=root&&id[cur]==-1) { vis[cur]=i; cur=pre[cur]; } if(cur!=root&&id[cur]==-1) {//找到一个环 e=pre[cur]; while(e!=cur) {//在一个环上的点都映射到新图上的同一个点 id[e]=cnt; e=pre[e]; } id[cur]=cnt++; } } if(cnt==0)//无环则返回答案 return ans; for(int i=0;i<n;++i)//每个点映射到新图的一个点 if(id[i]==-1) id[i]=cnt++; for(int i=0;i<m;++i) {//建立新图 s=edge[i].s; e=edge[i].e; edge[i].s=id[s]; edge[i].e=id[e]; if(id[s]!=id[e]) edge[i].w-=inEdge[e]; } n=cnt;//更新新图的点数 root=id[root];//更新新图的根 } return ans; } int main() { int a,c,L1,d,L2,money,num; int index[55][505];//index为每个课程点每个等级建立一个点 while(scanf("%d%d",&n,&m),n!=0||m!=0) { num=1; for(int i=1;i<=n;++i) {//每个课程每个等级映射到一个点 scanf("%d",&a); for(int j=0;j<=a;++j) index[i][j]=num++; index[i][502]=a; } for(int i=0;i<m;++i) { scanf("%d%d%d%d%d",&c,&L1,&d,&L2,&money); edge[i].s=index[c][L1]; edge[i].e=index[d][L2]; edge[i].w=money; } for(int i=1;i<=n;++i) { for(int j=1;j<=index[i][502];++j) {//每一课程相邻的两个等级,高等级到低等级有一条权值为0点有向边 edge[m].s=index[i][j]; edge[m].e=index[i][j-1]; edge[m++].w=0; } } for(int i=1;i<=n;++i) {//建立一个虚拟树根,到每个课程的0等级都有一条权值为0的有向边 edge[m].s=0; edge[m].e=index[i][0]; edge[m++].w=0; } n=num; printf("%d\n",Directed_MST(0)); } return 0; }