soj 3109

题意

中文题。

思路

由于选择实验和工具是必须在一起的,所以他们之间的连接的权值必须为无穷大。然后,用割对于实验来说,就是不要这个实验,而对于工具来说,就是选择这个工具。

所以,设置两个虚拟结点S,T。S连接所有的工具,权值为其花费,T连接所有的实验,权值为其利润。如果实验和工具之间有关系的话,那么他们之间连一条边,权值为无穷大。那么求得的最小割就是其总共花费的代价,用总共的利润-总共的代价即为答案。

这个代码我是用的dinic的多路增广在做的,给的时间是5000ms,我跑了100左右ms。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
#include<vector>
#define INF 1<<30
using namespace std;
const int MAXN=300;
int qq[MAXN],mark[MAXN],step[MAXN];
char s[MAXN];
int S,T,m,n,ss;
struct node
{
    int next;
    int value;
    int xx;
};
vector<node>w[MAXN];
bool bfs(int nn)
{
    memset(mark,0,sizeof(mark));
    memset(step,0,sizeof(step));
    int start=0,en=1;
    mark[S]=1;
    qq[0]=S;
    while(start<en)
    {
        int tt=qq[start++];
        int len=w[tt].size();
        for(int ii=0;ii<len;ii++)
        {
            int son=w[tt][ii].next;
            if(mark[son]||w[tt][ii].value<=0)continue;
            qq[en++]=son;
            mark[son]=1;
            step[son]=step[tt]+1;
        }
    }
     //for(int ii=0;ii<=m+n+1;ii++)cout<<step[ii]<<' ';
   // cout<<endl;
    if(mark[T]==1)return true;
    return false;
}//构建分层图
int dinic(int nn,int get_min)
{
    if(nn==T)return get_min;
    int total=0;
    int len=w[nn].size();
    for(int ii=0;ii<len&&get_min-total>0;ii++)
    {
        int son=w[nn][ii].next;
        if(step[son]!=step[nn]+1||w[nn][ii].value<=0)continue;
        int left=dinic(son,min(get_min-total,w[nn][ii].value));
        if(left==0)step[son]=-1;
        total+=left;
        w[nn][ii].value-=left;
        int aa=w[nn][ii].xx;
        w[son][aa].value+=left;
    }
    return total;
}
void addedge(int nn,int mm,int value)
{
    node ee;
    ee.next=mm;
    ee.value=value;
    ee.xx=w[mm].size();
    w[nn].push_back(ee);

    ee.value=0;
    ee.next=nn;
    ee.xx=w[nn].size()-1;
    w[mm].push_back(ee);
}//添加边,必须加入反向边,权值为0,之前没有注意这个value没有置0,wa了一次,呜呜。
void trans(int nn,char r[300])
{
    stack<int>q;
    int sta=0,dd=0;
    int len=strlen(r);
    while(sta<len)
    {
      while(r[sta]<='9'&&r[sta]>='0'){q.push(r[sta]-'0');sta++;}
      sta++;
      int sum=0,tt=1;
      while(!q.empty()){sum+=tt*q.top();q.pop();tt*=10;}
      if(dd==0){addedge(nn,T,sum);ss+=sum;}
      else addedge(sum+n,nn,INF);
      dd++;
    }
    return ;
}//提取数据,输入有点恶心
void init()
{
    for(int ii=0;ii<=n+m+1;ii++)w[ii].clear();
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        ss=0,S=0,T=n+m+1;
        getchar();
        for(int ii=1;ii<=n;ii++)
        {
           gets(s);
           trans(ii,s);
        }
        for(int ii=1;ii<=m;ii++)
        {
            int x;
            scanf("%d",&x);
            addedge(S,ii+n,x);//实验的标号1->n,工具的标号n+1->n+m。
        }
        int uu=0,ans=0;
        while(bfs(0))
            while(uu=dinic(0,INF))ans+=uu;
        printf("%d\n",ss-ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值