题意:有n个星球,星球上有可能有工厂或资源,星球之间有m条可以修建的通道,修建通道需要费用,现在要保证令尽可能多的工厂有资源供应的条件下,使得花费最小。
思路:有工厂和资源的星球很少,所以这题可以用状压解决,由于最终结果可能是森林,求完斯坦纳树后要合并,合并前后的每个状态都要满足工厂数大于等于资源数才能合并。比较坑爹的是最开始我以为工厂和资源都小于等于4,但是题里说的是有工厂的星球数不大于4,我这样的写法数组开小了就过不去,wa了好久。。。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=200+10;
const int maxm=5000+10;
struct Edge
{
int v,w,next;
Edge(){}
Edge(int v,int w,int next):v(v),w(w),next(next){}
}edges[maxm<<1];
struct Node
{
int u,st;
Node(){}
Node(int u,int st):u(u),st(st){}
};
int head[maxn],nEdge;
int d[maxn][1<<11],dp[1<<11],msk[maxn],type[15],n,m,K;
bool inq[maxn][1<<11];
queue<Node>q;
void AddEdges(int u,int v,int w)
{
edges[++nEdge]=Edge(v,w,head[u]);
head[u]=nEdge;
edges[++nEdge]=Edge(u,w,head[v]);
head[v]=nEdge;
}
void Init()
{
memset(head,0xff,sizeof(head));
nEdge=-1;
memset(d,0x3f,sizeof(d));
memset(dp,0x3f,sizeof(dp));
memset(inq,0,sizeof(inq));
memset(msk,0,sizeof(msk));
memset(type,0,sizeof(type));
int u,v,w;
K=0;
bool flag;
for(int i=1;i<=n;++i)
{
scanf("%d%d",&u,&v);
if(u||v) flag=true;
else flag=false;
while(u--)
{
msk[i]|=(1<<K);
type[K]=1;
K++;
}
if(v)
{
msk[i]|=(1<<K);
type[K]=-1;
K++;
}
if(flag) d[i][msk[i]]=0;
}
scanf("%d",&m);
for(int i=0;i<m;++i)
{
scanf("%d%d%d",&u,&v,&w);
AddEdges(u,v,w);
}
}
void spfa()
{
Node node;
while(!q.empty())
{
node=q.front();q.pop();
int u=node.u,state=node.st,v;
inq[u][state]=false;
for(int k=head[u];k!=-1;k=edges[k].next)
{
v=edges[k].v;
if(d[v][state|msk[v]]>d[u][state]+edges[k].w)
{
d[v][state|msk[v]]=d[u][state]+edges[k].w;
if((state|msk[v])==state&&!inq[v][state]) {inq[v][state]=true;q.push(Node(v,state));}
}
}
}
}
void Steiner_Tree()
{
for(int i=1;i<(1<<K);++i)
{
for(int j=1;j<=n;++j)
{
if(msk[j]&&((msk[j]&i)!=msk[j])) continue;
for(int k=i&(i-1);k;k=(k-1)&i)
d[j][i]=min(d[j][i],d[j][k|msk[j]]+d[j][(i-k)|msk[j]]);
if(d[j][i]<inf) {inq[j][i]=true;q.push(Node(j,i));}
}
spfa();
}
}
bool check(int x)
{
int ret=0;
for(int i=0;i<K;++i)
if(x&(1<<i)) ret+=type[i];
return ret>=0;
}
int cal(int x)
{
int ret=0;
for(int i=0;i<K;++i)
if(x&(1<<i)) ret+=(type[i]<0)?1:0;
return ret;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d",&n))
{
Init();
Steiner_Tree();
for(int i=0;i<(1<<K);++i)
for(int j=1;j<=n;++j)
dp[i]=min(dp[i],d[j][i]);
int maxf=0,minv=0;
for(int i=1;i<(1<<K);++i)
if(check(i))
{
for(int j=(i-1)&i;j;j=(j-1)&i)
{
if(check(j)&&check(i-j))
dp[i]=min(dp[i],dp[j]+dp[i-j]);
}
int tmp=cal(i);
if(dp[i]<inf&&(tmp>maxf||(tmp==maxf&&dp[i]<minv)))
{
maxf=tmp;
minv=dp[i];
}
}
if(maxf<=0) maxf=minv=0;
printf("%d %d\n",maxf,minv);
}
return 0;
}