题意:有n个星球,其中有些星球上有多个工作,有些星球上有些资源(但是一个星球上的资源只能提供给一个工厂),知道在一些星球边建立边的代价,问使得得到资源的工厂的数目最多的多少,在些前提下代价最小是多少。
还是斯坦纳树,不懂看:http://endlesscount.blog.163.com/blog/static/821197872012525113427573/
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int N=205; struct Edge { int v,w; Edge *nxt; }memo[N*N],*cur,*adj[N]; int n,m,mask; int d[N][1<<10],dp[1<<10]; bool vis[N][1<<10]; int fac[N],rec[N],st[N]; queue<int> que; void addEdge(int u,int v,int w) { cur->v=v; cur->w=w; cur->nxt=adj[u]; adj[u]=cur++; } void up(int &a,int b){ a=min(a,b); } bool judge(int s) { int cnt=0; for(int i=1;i<=n;i++) { if(s&st[i]) cnt+=fac[i],cnt-=rec[i]; } return cnt>=0; } int getnum(int s) { int cnt=0; for(int i=1;i<=n;i++) { if(s&st[i]) cnt+=rec[i]; } return cnt; } void spfa() { while(que.size()) { int u=que.front()/1000,s=que.front()%1000; que.pop(); vis[u][s]=0; for(Edge* it=adj[u];it;it=it->nxt) { int v=it->v,w=it->w; if(d[v][s|st[v]]>d[u][s]+w) { d[v][s|st[v]]=d[u][s]+w; if((s|st[v])==s&&!vis[v][s]) que.push(v*1000+s),vis[v][s]=1; } } } } void init() { cur=memo; memset(adj,0,sizeof(adj)); memset(vis,0,sizeof(vis)); memset(dp,63,sizeof(dp)); memset(d,63,sizeof(d)); memset(st,0,sizeof(st)); } int main() { while(scanf("%d",&n)!=EOF) { init(); int ans=0,fac_cnt=0,rec_cnt=0; for(int i=1;i<=n;i++) { scanf("%d%d",&fac[i],&rec[i]); if(fac[i]&&rec[i]) fac[i]--,rec[i]--,ans++; if(fac[i]) { st[i]=(1<<(fac_cnt++)); d[i][st[i]]=0; } if(rec[i]) { st[i]=(1<<(4+rec_cnt++)); d[i][st[i]]=0; } } scanf("%d",&m); while(m--) { int u,v,w; scanf("%d%d%d",&u,&v,&w); addEdge(u,v,w); addEdge(v,u,w); } mask=(1<<(4+rec_cnt))-1; for(int s=1;s<=mask;s++) { for(int i=1;i<=n;i++) { if(st[i]&&!(st[i]&s)) continue; for(int p=(s-1)&s;p;p=(p-1)&s) { int s1=p|st[i],s2=(s-p)|st[i]; up(d[i][s],d[i][s1]+d[i][s2]); } if(d[i][s]<1e9&&!vis[i][s]) que.push(i*1000+s),vis[i][s]=1; } spfa(); } for(int s=1;s<=mask;s++) for(int i=1;i<=n;i++) dp[s]=min(dp[s],d[i][s]); int cnt=0,cost=0; for(int s=1;s<=mask;s++) { if(judge(s)&&dp[s]<1e9) { for(int p=(s-1)&s;p;p=(p-1)&s) { if(judge(p)&&judge(s-p)&&dp[p]<1e9&&dp[s-p]<1e9) up(dp[s],dp[p]+dp[s-p]); } int tmp=getnum(s); if(cnt<tmp||(cnt==tmp&&cost>dp[s])) cnt=tmp,cost=dp[s]; } } printf("%d %d\n",cnt+ans,cost); } return 0; }