ZOJ 3613

斯坦纳树

#include<cstdio>
#include<cstring>
#include<queue>
#define N 210
#define M 5100
using namespace std;
int a[N],b[N];
int a1[N],b1[N];
int st[N],dp[N][1000],ans[1000];
bool vis[N][1000];
struct Edge{
    int v,w,next;
}edge[M*2];
int n,m,k,K,ans2;
int head[N],cnt;
queue<int>q;
void addedge(int u,int v,int w){
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].v=u;
    edge[cnt].w=w;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}
void init(){
    memset(head,-1,sizeof(head));
    memset(st,0,sizeof(st));
    memset(dp,-1,sizeof(dp));
    memset(vis,0,sizeof(vis));
    memset(ans,-1,sizeof(ans));
    cnt=0;
}
void SPFA(){
    int i;
    while(!q.empty()){
        int x=q.front()/10000,y=q.front()%10000;
        vis[x][y]=0;
        q.pop();
        for(i=head[x];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(dp[v][st[v]|y]==-1 || dp[v][st[v]|y]>dp[x][y]+edge[i].w){
                dp[v][st[v]|y]=dp[x][y]+edge[i].w;
                if(!vis[v][st[v]|y] && (st[v]|y)==y){
                    vis[v][st[v]|y]=1;
                    q.push(v*10000+(st[v]|y));            //一定要加括号
                }
            }
        }
    }
}
void Steiner_Tree(){
    int i,j,p,x;
    for(p=0;p<K;p++){
        for(i=1;i<=n;i++){
            if(st[i] && (st[i]&p)==0)continue;
            for(x=(p-1)&p;x;x=(x-1)&p)
                if(dp[i][x|st[i]]!=-1 && dp[i][(p-x)|st[i]]!=-1)
                    if(dp[i][p]==-1 || (dp[i][p]>dp[i][x|st[i]]+dp[i][(p-x)|st[i]]))
                        dp[i][p]=dp[i][x|st[i]]+dp[i][(p-x)|st[i]];
            if(dp[i][p]!=-1){
                q.push(i*10000+p);
                vis[i][p]=1;
            }
        }
        SPFA();
    }
}
bool check(int u){
    int i,a2=0,b2=0;
    for(i=0;i<k;i++)
        if(u&(1<<i)){
            a2+=a1[i];
            b2+=b1[i];
        }
    return b2<=a2;
}
int cout(int u){
    int i,a2=0,b2=0;
    for(i=0;i<k;i++)
        if(u&(1<<i)){
            a2+=a1[i];
            b2+=b1[i];
        }
    return min(a2,b2);
}
void solve(){
    int i,j;
    int num=0,ans1=0;
    for(i=0;i<K;i++)
        for(j=1;j<=n;j++)
            if(dp[j][i]!=-1)
                if(ans[i]==-1 || ans[i]>dp[j][i])
                    ans[i]=dp[j][i];
    for(i=0;i<K;i++){
        //if(check(i)){           //这里只需去找那些工厂大于等于resource planets个数的状态即可
            for(j=(i-1)&i;j;j=(j-1)&i)
                if((check(i) && check(j) && check(i-j)) || (!check(i) && !check(j) && !check(i-j))) //或者找划分中工厂个数与resource planets个数不等式同方向的
                    if(ans[j]!=-1 && ans[i-j]!=-1)
                        if(ans[i]==-1 || ans[i]>ans[j]+ans[i-j])
                            ans[i]=ans[j]+ans[i-j];
            int cou=cout(i);
            if(ans[i]!=-1)    //一定要加这个判断
                if(cou>num || (cou==num && ans[i]<ans1))
                    num=cou,ans1=ans[i];
        //}
    }
    printf("%d %d\n",num/*+ans2*/,ans1);
}
int main(){
    int i,u,v,w;
    while(scanf("%d",&n)==1){
        k=ans2=0;
        init();
        for(i=1;i<=n;i++){
            scanf("%d %d",&a[i],&b[i]);
            /*if(a[i]&&b[i]){
                a[i]--;
                b[i]--;
                ans2++;
            }*/
            if(a[i]||b[i]){
                a1[k]=a[i],b1[k]=b[i];
                st[i]=1<<(k++);
                dp[i][st[i]]=0;
            }
        }
        K=1<<k;
        scanf("%d",&m);
        for(i=1;i<=m;i++){
            scanf("%d %d %d",&u,&v,&w);
            addedge(u,v,w);
        }
        Steiner_Tree();
        solve();
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值