2015-2016 下半学期 第五周 训练

来源:互联网 发布:麻依坊服装淘宝 编辑:程序博客网 时间:2024/05/19 19:32

1、hdu4411

题意:

有N+1个点,每个点与编号大于自己的点之间有一条有权边(权重通过floyd求得),现有k个人位于0处,要从k个人中选出若干个人遍历其它点并最终回到0点,使每个点(除0外)都被访问恰好一次,问最小费用之和为多少。

题解:

每个点至多走一次,显然需要把一个点拆成两个,一个出点一个入点之间费用为0流量为1,超级源点拆为流量为k费用为距离的边,由于原图无环,所以可以将i和i‘之间的费用设为-M,流量设为1,M应该大于源点和汇点间最长链的长度。由于每次都找最短路径,因此这些边一定会被有限考虑,因此可以保证这些边恰好走了一次。为了保证不走增广后产生的负权反向边,源点和汇点之间连一条流量为k费用为0的点。

代码:

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<queue>using namespace std;#define maxn 1010#define maxm 20010const int inf = 0x3f3f3f3f;struct Nod {int b, nxt;int cap, cst;void init(int b, int nxt, int cap, int cst) {this->b = b;this->nxt = nxt;this->cap = cap;this->cst = cst;}};struct MinCost {int E[maxn];int n;Nod buf[maxm * 2];int len;int p[maxn];void init(int n) {this->n = n;memset(E, 255, sizeof(E));len = 0;}void addCap(int a, int b, int cap, int cst) {//printf("%d %d %d\n",a,b,cst);buf[len].init(b, E[a], cap, cst);E[a] = len++;buf[len].init(a, E[b], 0, -cst);E[b] = len++;}bool spfa(int source, int sink) {static queue<int> q;static int d[maxn];memset(d, 63, sizeof(d));memset(p, 255, sizeof(p));d[source] = 0;q.push(source);int u, v;while (!q.empty()) {u = q.front();q.pop();for (int i = E[u]; i != -1; i = buf[i].nxt) {v = buf[i].b;if (buf[i].cap > 0 && d[u] + buf[i].cst < d[v]) {d[v] = d[u] + buf[i].cst;p[v] = i;q.push(v);}}}return d[sink] != inf;}int solve(int source, int sink) {int minCost = 0, maxFlow = 0;//需要maxFlow的话,想办法返回while (spfa(source, sink)) {int neck = inf;for (int t = p[sink]; t != -1; t = p[buf[t ^ 1].b])//buf[t^壹].b是父节点neck = min(neck, buf[t].cap);maxFlow += neck;for (int t = p[sink]; t != -1; t = p[buf[t ^ 1].b]) {buf[t].cap -= neck;//printf("%d\n",buf[t].b);buf[t ^ 1].cap += neck;minCost += buf[t].cst * neck;}//printf("-----\n");}return minCost;}} mc;int map[110][110],n;void floyd() {for (int k =0; k <= n; k++)for (int i =0; i <= n; i++)for (int j=0; j <= n; j++){if (map[i][k] + map[k][j] < map[i][j])map[i][j] = map[i][k]+map[k][j];}}int main() {int m, k, a, b, c;while (scanf("%d%d%d",&n,&m,&k)&&n) {for (int i = 0; i <= n; i++)for (int j = 0;j<= n; j++)map[i][j] = inf;while (m--) {scanf("%d%d%d", &a, &b, &c);map[b][a]=map[a][b] = min(map[a][b],c);}floyd();mc.init(n * 2 + 3);mc.addCap(0, n * 2 + 1, k, 0);mc.addCap(n*2+1,n*2+2,k,0);int temp=1<<23;for (int i = 1; i <= n; i++) {mc.addCap(i, i + n,1,-temp);mc.addCap(n*2+1,i,1,map[i][0]);mc.addCap(i+n, n*2+2,1,map[i][0]);for (int j = i+1;j<=n;j++)mc.addCap(i+n,j,1,map[i][j]);}printf("%d\n",mc.solve(0,n*2+2)+temp*n);}return 0;}

2、hdu4971

题意:

n(n <= 20)个项目,m(m <= 50)个技术问题,做完一个项目可以有收益profit (<= 1000),做完一个项目必须解决相应的技术问题,解决一个技术问题需要付出cost ( <= 1000),技术问题之间有先后依赖关系,求最大收益。

题解:

每个点有权值,点之间存在依赖关系,所以是最大权闭合图。对于最大权闭合图我们的解决方法一般是,从S向所有点权为正的点连边,边权为点权,从所有点权为负的点向T连边,边权为点权的绝对值,原图中的边量为inf。

然后我们花几个图可以发现 所有点权为正的点权和-最小割=最大权,然后转换为最大流问题。

代码:

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<iostream>#include<queue>#include<vector>#include<set>#include<stack>#include<map>#include<ctime>#include<bitset>#define LL long  long#define db double#define EPS 1e-15#define inf 1e10using namespace std;const int maxn = 100;const int INF = 0x3f3f3f3f;struct arc{    int to,flow,next;    arc(int x = 0,int y = 0,int z = -1){        to = x;        flow = y;        next = z;    }}e[maxn*maxn];int head[maxn],d[maxn],cur[maxn],tot,S,T;void add(int u,int v,int flow){    e[tot] = arc(v,flow,head[u]);    head[u] = tot++;    e[tot] = arc(u,0,head[v]);    head[v] = tot++;}bool bfs(){    queue<int>q;    memset(d,-1,sizeof d);    d[S] = 0;    q.push(S);    while(!q.empty()){        int u = q.front();        q.pop();        for(int i = head[u]; ~i; i = e[i].next){            if(e[i].flow && d[e[i].to] == -1){                d[e[i].to] = d[u] + 1;                q.push(e[i].to);            }        }    }    return d[T] > -1;}int dfs(int u,int low){    if(u == T) return low;    int tmp = 0,a;    for(int &i = cur[u]; ~i; i = e[i].next){        if(e[i].flow && d[e[i].to] == d[u]+1&&(a=dfs(e[i].to,min(e[i].flow,low)))){            e[i].flow -= a;            low -= a;            e[i^1].flow += a;            tmp += a;            if(!low) break;        }    }    if(!tmp) d[u] = -1;    return tmp;}int dinic(){    int ret = 0;    while(bfs()){        memcpy(cur,head,sizeof head);        ret += dfs(S,INF);    }    return ret;}int main(){    int Ts,n,m,u,v,w,k,ret,cs = 1;    scanf("%d",&Ts);    while(Ts--){        memset(head,-1,sizeof head);        scanf("%d %d",&n,&m);        tot = ret = S = 0;        T = n + m + 1;        for(int i = 1; i <= n; ++i){            scanf("%d",&w);            add(S,i,w);            ret += w;        }        for(int i = 1; i <= m; ++i){            scanf("%d",&w);            add(i+n,T,w);        }        for(int i = 1; i <= n; ++i){            scanf("%d",&k);            while(k--){                scanf("%d",&u);                add(i,u + n + 1,INF);            }        }        for(int i = 1; i <= m; ++i)        for(int j = 1; j <= m; ++j){            scanf("%d",&w);            if(w) add(i+n,j+n,INF);        }        printf("Case #%d: %d\n",cs++,ret - dinic());    }    return 0;}

3、poj1087

题意:

一堆插头,一堆用电器,一堆插座,一堆转换器,问你最多能满足几个。

题解:

虽然是网络流专题里的,我用二分图匹配做的。用电器一个集合,插座一个集合,转换器负责连边,因为可能存在鬼畜的转换器,所以floyd传递一下闭包,然后跑二分图最大匹配。

网上的网络流题解那个反向边连法我没看懂。

代码:

#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>#include <iostream>#include <queue>#include <vector>#include <set>#include <stack>#include <map>#include <ctime>#include <bitset>#define LL long  long#define db double#define EPS 1e-15#define inf 1e10using namespace std;const int MAXN=200+100;int n,m,k,cnt,edge_cnt;char str[25],s[5],s1[5];int vis[2*MAXN],a[MAXN],head[2*MAXN],link[2*MAXN],d[2*MAXN][2*MAXN];map<string,int>mp;struct Edge{    int v;    int next;}edge[MAXN*MAXN];void init(){    edge_cnt=0;    mp.clear();    memset(head,-1,sizeof(head));    memset(link,-1,sizeof(link));    memset(vis,0,sizeof(vis));    memset(d,0,sizeof(d));}void floyd(){    for(int k=1;k<=m+cnt;k++)        for(int i=1;i<=m+cnt;i++)            for(int j=1;j<=m+cnt;j++)                if(d[i][k] && d[k][j] && !d[i][j])                    d[i][j]=1;}void addedge(int u,int v){    edge[edge_cnt].v=v;    edge[edge_cnt].next=head[u];    head[u]=edge_cnt++;}int path(int u){    for(int i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].v;        if(vis[v])            continue;        vis[v]=1;        if(link[v]==-1 || path(link[v])){            link[v]=u;            return 1;        }    }    return 0;}int main(){    while(~scanf("%d",&n)){        init();        for(int i=1;i<=n;i++){            scanf("%s",s);            mp[s]=i;        }        cnt=n;        scanf("%d",&m);        for(int i=1;i<=m;i++){            scanf("%s%s",str,s);            if(!mp[s])                mp[s]=++cnt;           d[i][m+mp[s]]=1;        }        scanf("%d",&k);        for(int i=0;i<k;i++){            scanf("%s%s",s,s1);            if(!mp[s])                mp[s]=++cnt;            if(!mp[s1])                mp[s1]=++cnt;            d[m+mp[s]][m+mp[s1]]=1;        }        floyd();        for(int i=1;i<=m;i++)            for(int j=m+1;j<=m+n;j++)                if(d[i][j])                    addedge(i,j);        int res=0;        for(int i=1;i<=m;i++){            memset(vis,0,sizeof(vis));            res+=path(i);        }        printf("%d\n",m-res);    }    return 0;}

4、zoj2314

题意:

给n个点,及m根pipe,每根pipe用来流躺液体的,单向的,每时每刻每根pipe流进来的物质要等于流出去的物质,要使得m条pipe组成一个循环体,里面流淌物质。并且满足每根pipe一定的流量限制,范围为[Li,Ri].即要满足每时刻流进来的不能超过Ri(最大流问题),同时最小不能低于Li。

题解:

令每一个点流进来的流=流出去的流,对于每一个点i,令Mi= sum(i点所有流进来的下界流)– sum(i点所有流出去的下界流)

如果Mi大于0,代表此点必须还要流出去Mi的自由流,那么我们从源点连一条Mi的边到该点。

如果Mi小于0,代表此点必须还要流进来Mi的自由流,那么我们从该点连一条Mi的边到汇点。

如果求S->T的最大流,看是否满流(S的相邻边都流满)。满流则有解,否则无解

代码:

#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>#include <iostream>#include <queue>#include <vector>#include <set>#include <stack>#include <map>#include <ctime>#include <bitset>#define LL long  long#define db double#define EPS 1e-15#define inf 1e10using namespace std;int cnt;struct edge{int to,w,nxt;}e[100005];int head[205],cur[205],in[205],lv[205];int q[205],n,m,low[100005];const int T=201;bool bfs(){for (int i=1;i<=T;i++) lv[i]=-1;int st=0,ed=1;q[0]=0,lv[0]=0;while (st!=ed){int now=q[st];st++;for (int i=head[now];i!=-1;i=e[i].nxt)if (e[i].w && lv[e[i].to]==-1){lv[e[i].to]=lv[now]+1;q[ed++]=e[i].to;}}if (lv[T]==-1) return 0;return 1;}int dfs(int x,int flow){if (x==T) return flow;int used=0,w;for (int i=cur[x];i!=-1;i=e[i].nxt)if (lv[e[i].to]==lv[x]+1){w=flow-used;w=dfs(e[i].to,min(e[i].w,w));e[i].w-=w; e[i^1].w+=w;if (e[i].w) cur[x]=i;used+=w;if (used==flow) return flow;}if (!used) lv[x]=1;return used;}void dinic(){while (bfs()){for (int i=0;i<=T;i++)cur[i]=head[i];dfs(0,inf);}}void insert(int u,int v,int w){e[++cnt].to=v, e[cnt].w=w;e[cnt].nxt=head[u], head[u]=cnt;e[++cnt].to=u, e[cnt].w=0;e[cnt].nxt=head[v], head[v]=cnt;}void build(){for (int i=1;i<=n;i++)if (in[i]>0) insert(0,i,in[i]);else insert(i,T,-in[i]);}bool judge(){for (int i=head[0];i!=-1;i=e[i].nxt)if (e[i].w) return 0;return 1;}int main(){while (scanf("%d%d",&n,&m)!=EOF){cnt=1;memset(head,-1,sizeof(head));memset(in,0,sizeof(in));for (int i=1;i<=m;i++){int u,v,w;scanf("%d%d%d%d",&u,&v,&low[i],&w);in[u]-=low[i],in[v]+=low[i];insert(u,v,w-low[i]);}build();dinic();if (!judge()) printf("NO\n");else {printf("YES\n");for (int i=1;i<=m;i++)printf("%d\n",e[(i<<1)^1].w+low[i]);}}return 0;}

5、nefu500

题意:中文题。

题解:

题目问你最大值最小,显然是二分,二分边权最大值,如果小于就连边,跑最大流,如果慢流就说明可以。

代码:

玛德re是以为你cnt每次二分忘记初始化了,WA是因没有保留那个最小的cnt1。

#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<iostream>#include<queue>#include<vector>#include<set>#include<stack>#include<map>#include<ctime>#include<bitset>#define LL long  long#define db double#define EPS 1e-15#define inf 1e9using namespace std;struct edges{    int u,to,w,nxt;}e[200005];int cnt=0,T,S,n,m;int head[200005],lv[200005],cur[200005];void init(int nn){    S=1,T=nn+1;    memset(head,-1,sizeof(head));}void add(int u,int v,int w){    e[cnt].to=v, e[cnt].w=w;    e[cnt].nxt=head[u], head[u]=cnt++;    e[cnt].to=u, e[cnt].w=0;    e[cnt].nxt=head[v], head[v]=cnt++;}int q[200005];bool bfs(){    for (int i=0;i<=T;i++) lv[i]=-1;    int st=0,ed=0;    lv[q[ed++]=1]=0;    while (st!=ed){        int now=q[st];st++;        for (int i=head[now];i!=-1;i=e[i].nxt)            if (e[i].w && lv[e[i].to]==-1){                lv[e[i].to]=lv[now]+1;                q[ed++]=e[i].to;                if (e[i].to==T) return 1;            }    }    return 0;}int dfs(int x,int flow){    if (x==T) return flow;    int used=0,w;    for (int &i=cur[x],w;i!=-1;i=e[i].nxt)        if(e[i].w && lv[e[i].to]==lv[x]+1 && (used=dfs(e[i].to,min(flow,e[i].w)))>0){            e[i].w-=used;            e[i^1].w+=used;            return used;        }    return 0;}int dinic(){    int ret=0,delta;    while (bfs()){        for (int i=0;i<=T;i++)            cur[i]=head[i];        while(delta=dfs(1,inf)) ret+=delta;    }    return ret;}int a[200005],sum;struct de{    int u,v,w,lim;}node[200005];int main(){    while (scanf("%d%d",&n,&m)!=EOF){        int sum=0;        for (int i=1;i<=n;i++){            scanf("%d",&a[i]);            sum+=a[i];        }        int maxx=0;        for (int i=1;i<=m;i++){            scanf("%d%d%d%d",&node[i].u,&node[i].v,&node[i].w,&node[i].lim);            maxx=max(maxx,node[i].w);        }        int l=0,r=maxx+1,flag=0;        int cnt1=inf;        while (l<=r) {            //puts("yes");            cnt=0;            int mid=(l+r)/2;            init(n);            for (int i=1;i<=m;i++){                if (node[i].w<=mid)                    add(node[i].u,node[i].v,node[i].lim);            }            for (int i=1;i<=n;i++){                if (a[i]==0) continue;                else add(i,T,a[i]);            }            int ans=dinic();            if (ans==sum){                flag=1;                cnt1=min(mid,cnt1);                r=mid-1;            }            else l=mid+1;        }        if (flag) printf("%d\n",cnt1);        else printf("-1\n");    }    return 0;}



0 0
原创粉丝点击