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

1、hdu4292

题解:最大流 因为我们发现食物和饮料间没有直接的联系,所以要用人来当中间点,但是如果直接连到人上,会让食物和饮料流出奇怪的情况,所以把人拆点。

    人和人之间连边权为1,食物和人 饮料和人都连边为1。
    食物和S连食物个数,饮料和T连饮料个数。超级源点->食物->人->人'->饮料->超级汇点。

代码:

#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 0x3f3f3f3f

using namespace std;

const int N = 20005;
const int M = 200005;
int n, f, d, s, t;
int cnt, head[N], link[N], que[N], lv[N];
int nxt[M], to[M], v[M];
void add(int u,int vv,int w){
    to[cnt]=vv,v[cnt]=w;
    nxt[cnt]=head[u],head[u]=cnt++;

    to[cnt]=u,v[cnt]=0;
    nxt[cnt]=head[vv],head[vv]=cnt++;
}
int bfs(){
    int kid,now,f=0,r=1,i;
    memset(lv,0,sizeof(lv));
    que[0]=s; lv[s]=1;
    while (f<r){
        now=que[f++];
        for(int i=head[now];i!=-1;i=nxt[i]){
            kid=to[i];
            if (!lv[kid] && v[i]){
                lv[kid]=lv[now]+1;
                if (kid==t) return 1;
                que[r++]=kid;
            }
        }
    }
    return 0;
}
int dfs(int now,int sum){
    int kid,flow,rt=0;
    if (now==t) return sum;
    for (int i=link[now];i!=-1 && rt<sum;i=nxt[i]){
        link[now]=i;
        kid=to[i];
        if (lv[kid]==lv[now]+1 && v[i]){
            flow=dfs(kid,min(sum-rt,v[i]));
            if (flow){
                v[i]-=flow;
                v[i^1]+=flow;
                rt+=flow;
            }
            else lv[kid]=-1;
        }
    }
    return rt;
}
char c[205];
void read(){
    int a;
    for (int i=1;i<=f;i++){
        scanf("%d",&a);
        add(s,i,a);
    }
    for (int i=1;i<=d;i++){
        scanf("%d",&a);
        add(i+900,t,a);
    }
    for (int i=1;i<=n;i++)
        add(i+300,i+600,1);
    getchar();
    for (int i=1;i<=n;i++){
        scanf("%s",c);
        for (int j=0;j<f;j++)
            if (c[j]=='Y')
                add(j+1,i+300,1);
    }
    for (int i=1;i<=n;i++){
        scanf("%s",c);
        for (int j=0;j<d;j++)
            if (c[j]=='Y')
                add(i+600,j+901,1);
    }
}
int dinic(){
    int ans=0;
    while (bfs()){
        for (int i=0;i<=t;i++)
            link[i]=head[i];
        ans+=dfs(s,inf);
    }
    return ans;
}
int main(){
    while (scanf("%d%d%d",&n,&f,&d)==3){
        cnt=0;
        memset(head,-1,sizeof(head));
        s=0,t=1999;
        read();
        printf("%d\n",dinic());
    }
    return 0;
}


2、hdu5249

题目:有一张图,其中n个点,m条双向边,边有权值。所以从起点1走到终点n有一个最短时间。问:
(1)最少去掉多少条边,使得从起点到终点的时间大于最短时间(走不到也算)
(2)最多去掉多少条边,使得从起点到终点的时间仍然为最短时间。
其中 n ≤ 2000; m ≤ 60000

题解:第二问明显是m-(边最少的最短路径上的边数),第一问我们可以联想到是一个最小割,然后最小割最大流定理,构造边流量为1的新图,跑最大流。

代码:

#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 0x3f3f3f3f

using namespace std;

const int maxn = 2010;
const int maxm = 60010;
const int N = 2010;
const int M = 60010;
struct node{
    int v,c,flow,nxt;
}e[120020];
vector< pair<int, int> > G[N];
queue<int> Q;
int head[maxn],cnt,n,m;
int st,ed;
int dd[maxn],cc[maxn];
void spfa(int st,int ed){
    dd[st]=cc[st]=0;
    bool vis[maxn]={0};
    while (!Q.empty()) Q.pop();
    vis[st]=1;
    Q.push(st);
    while (!Q.empty()){
        int u=Q.front(); Q.pop();
        vis[u]=0;
        for (int i=0;i<G[u].size();i++){
            int v=G[u][i].first,c=G[u][i].second;
            if (dd[u]+c==dd[v]) cc[v]=min(cc[v],cc[u]+1);
            if (dd[u]+c<dd[v]){
                cc[v]=cc[u]+1;
                dd[v]=dd[u]+c;
                if (!vis[v]) Q.push(v), vis[v]=1;
            }
        }
    }
}
void add(int u,int v,int c){
    e[++cnt].v=v; e[cnt].c=c;
    e[cnt].flow=0; e[cnt].nxt=head[u];
    head[u]=cnt;
    e[++cnt].v=u; e[cnt].c=0;
    e[cnt].flow=0; e[cnt].nxt=head[v];
    head[v]=cnt;
}
void init(){
    memset(head,-1,sizeof(head));
    cnt=1;
    st=1, ed=n;
    for (int u=1;u<=n;u++){
        for (int i=0;i<G[u].size();i++){
            if (dd[u]+G[u][i].second==dd[G[u][i].first])
                add(u,G[u][i].first,1);
        }
    }
}
int lv[maxn],num[maxn],q[maxn];
void bfs(){
    int f=0,r=1;
    for (int i=0;i<=n;i++){
        lv[i]=0;
        num[i]=0;
    }
    q[f]=st;
    lv[st]=1;
    num[st]=1;
    while (f!=r){
        int u=q[f++];
        for (int i=head[u];i;i=e[i].nxt){
            if (e[i^1].c==0 || lv[e[i].v]<maxn) continue;
            lv[e[i].v]=lv[u]+1;
            ++num[lv[e[i].v]];
            q[r++]=e[i].v;
        }
    }
}
int dinic(){
    n+=3;
    bfs();
    int u,flow=0;
    int cur[maxn],path[maxn];
    for (int i=0;i<=n;i++)
        cur[i]=head[i];
    u=st;
    while (lv[st]<n){
        if (u==ed){
            int augflow=inf;
            for (int i=st;i!=ed;i=e[cur[i]].v)
                augflow=min(augflow,e[cur[i]].c);
            for (int i=st;i!=ed;i=e[cur[i]].v){
                e[cur[i]].c-=augflow;
                e[cur[i]^1].c+=augflow;
                e[cur[i]].flow+=augflow;
                e[cur[i]^1].flow-=augflow;
            }
            flow+=augflow;
            u=st;
        }
        int i;
        for (i=cur[u];i;i=e[i].nxt)
            if(e[i].c>0 && lv[u]==lv[e[i].v]+1) break;
        if (i){
            cur[u]=i;
            path[e[i].v]=i^1;
            u=e[i].v;
        }
        else {
            if (0==(--num[lv[u]])) break;
            cur[u]=head[u];
            int mindis=n;
            for (int j=head[u];j;j=e[j].nxt)
                if(e[j].c>0) mindis=min(mindis,lv[e[j].v]);
            lv[u]=mindis+1;
            ++num[lv[u]];
            if (u!=st)
                u=e[path[u]].v;
        }
    }
    n-=3;
    return flow;
}
int main(){
    while (~scanf("%d%d",&n,&m)){
        for (int i=1;i<=n;i++){
            G[i].clear();
            dd[i]=cc[i]=inf;
        }
        int u,v,l;
        for (int i=0;i<m;i++){
            scanf("%d%d%d",&u,&v,&l);
            G[u].push_back({v,l});
            G[v].push_back({u,l});
        }
        spfa(1,n);
        init();
        printf("%d %d\n",dinic(),m-cc[n]);
    }
    return 0;
}

3、hdu4289

题目:有n个城市,有个小偷想从其中一个城市逃到另一个城市,警察想要堵截这个小偷,知道了在每个城市堵截的成本,问如何安排在哪些城市堵截可以使小偷一定会被抓住,而且成本最低。

题解:还是最小割模型,把城市拆点,然后有边的两个城市连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 1e10

using namespace std;

struct edge{
    int to,w,nxt;
}e[20005*8];
int lv[2005],que[2005],val[2005];
int head[2005],cnt;
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 bfs(int s,int t){
    memset(lv,0,sizeof(lv));
    int f=0,tail=1;
    lv[s]=1; que[f]=s;
    while (f<tail){
        int now=que[f++];
        for (int i=head[now];i!=-1;i=e[i].nxt){
            if (e[i].w && lv[e[i].to]==0){
                lv[e[i].to]=lv[now]+1;
                if (e[i].to==t) return 1;
                que[tail++]=e[i].to;
            }
        }
    }
    return 0;
}
int dfs(int s,int t,int flow){
    if (s==t) return flow;
    int tmp,cost=0;
    for (int i=head[s];i!=-1;i=e[i].nxt){
        if (e[i].w && lv[e[i].to]==lv[s]+1){
            tmp=dfs(e[i].to,t,min(flow-cost,e[i].w));
            if (tmp>0){
                e[i].w-=tmp;
                e[i^1].w+=tmp;
                cost+=tmp;
                if (flow==cost) break;
            }
            else lv[e[i].to]=-1;
        }
    }
    return cost;
}
int dinic(int s,int t){
    int ans=0;
    while (bfs(s,t))
        ans+=dfs(s,t,inf);
    return ans;
}
int main(){
    int n,m,s,t;
    while (~scanf("%d%d",&n,&m)){
        cnt=0;
        memset(head,-1,sizeof(head));
        scanf("%d%d",&s,&t);
        for (int i=1;i<=n;i++){
            scanf("%d",&val[i]);
            add(i,i+1000,val[i]);
        }
        int u,v;
        while (m--){
            scanf("%d%d",&u,&v);
            add(u+1000,v,inf);
            add(v+1000,u,inf);
        }
        int ss=2003,tt=2004;
        add(ss,s,inf);
        add(t+1000,tt,inf);
        s=ss,t=tt;
        printf("%d\n",dinic(s,t));
    }
    return 0;
}

4、hdu4888 & hdu4975

题目:给一个N行M列的数字矩阵的行和以及列和,每个元素的大小不超过9,问这样的矩阵是否存在,是否唯一。

题解:这个题问是否存在很好想,S到行和连行和,列和到T连列和,行和到列和连9,但是问是否唯一这里我真的想了好久……之前一直觉得复杂度不对……后来发现要对残量网络删边建新图然后找环就是O(M)的了,如果不建新图,每条边要被判(N-1)*(M-1)+1次……为什么找环就能确定唯一呢……因为如果你图上有一个流量为正点数为偶数的环,说明数值可以在其中两个点上流动,所以就不唯一。

代码:

#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 1e10

using namespace std;

const int MAXN=500*2+10;
const int MAXM=500*500+2*MAXN;
int n,m,sum;
int S,T;
int head[MAXN],cnt;
int cur[MAXN],lv[MAXN];
bool flag,only;
struct edge{
    int from,to,w,flow,nxt;
}e[MAXM<<1];
void add(int u,int v,int w){
    e[cnt].from=u;
    e[cnt].to=v; e[cnt].w=w; e[cnt].flow=0;
    e[cnt].nxt=head[u]; head[u]=cnt++;
    e[cnt].from=v;
    e[cnt].to=u; e[cnt].w=0; e[cnt].flow=0;
    e[cnt].nxt=head[v]; head[v]=cnt++;
}
bool bfs(){
    puts("yes");
    memset(lv,-1,sizeof(lv));
    queue<int>q;
    q.push(S);
    lv[S]=0;
    while (!q.empty()){
        int now=q.front();
        q.pop();
        for (int i=head[now];i!=-1;i=e[i].nxt){
            int v=e[i].to;
            if (lv[v]==-1 && e[i].w>e[i].flow){
                lv[v]=lv[now]+1;
                q.push(v);
            }
        }
    }
    return lv[T]!=-1;
}
int dfs(int u,int cap){
    if (u==T || cap==0) return cap;
    int flow=0,subflow;
    for (int i=cur[u];i!=-1;i=e[i].nxt){
        cur[u]=i;
        int v=e[i].to;
        if ((lv[v]==lv[u]+1) && (subflow=dfs(v,min(cap,e[i].w-e[i].flow)))>0){
            flow+=subflow;
            e[i].flow+=subflow;
            e[i^1].flow-=subflow;
            cap-=subflow;
            if(cap==0) break;
        }
    }
    return flow;
}
int dinic(){
    int maxflow=0;
    while(bfs()){
        memcpy(cur,head,sizeof(head));
        maxflow+=dfs(S,inf);
    }
    return maxflow;
}
void check1(){
    if (flag) return ;
    if (dinic()!=sum){
        flag=1;
    }
}
bool vis[MAXN];
bool checkO(int x,int f){
    vis[x]=1;
    for (int i=head[x];i;i=e[i].nxt){
        int v=e[i].to;
        if (v!=f){
            if (vis[v]) return 1;
            if (checkO(v,x)) return 1;
        }
    }
    vis[x]=0;
    return 0;
}
void checkonly(){
    if (flag) return ;
    memset(vis,0,sizeof(vis));
    for (int i=1;i<=n;i++){
        if (checkO(i,-1)){
            only=0;
            return ;
        }
    }
}
void add2(int u,int v){
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt++;
}
void rebuild(){
    memset(head,-1,sizeof(head));
    int tot=cnt;
    cnt=0;
    for (int i=0;i<tot;i++){
        if (e[i].w>e[i].flow){
            add2(e[i].from,e[i].to);
        }
    }
}
int main(){
    int T,cas=1;
    scanf("%d",&T);
    while (T--){
        scanf("%d %d",&n,&m);
        flag=0,only=1;
        cnt=0;
        memset(head,-1,sizeof(head));
        int r,c,rsum=0,csum=0;
        S=0; T=n+m+1;
        for (int i=1;i<=n;++i){
            scanf("%d",&r);
            rsum+=r;
            add(S,i,r);
        }
        for (int i=1;i<=m;++i){
            scanf("%d",&c);
            csum+=c;
            add(i+n,T,c);
        }
        if (rsum!=csum){
            flag=1;
            break;
        }
        sum=rsum;
        for (int i=1;i<=n;i++){
            for (int j=m;j>=1;j--){
                add(i,j+n,9);
            }
        }
        check1();
        rebuild();
        checkonly();
        printf("Case #%d: ",cas++);
        if(flag) puts("So naive!");
        else if (only) puts("So simple!");
        else puts("So young!");
    }
    return 0;
}




你可能感兴趣的:(2015-2016 下半学期 第四周 训练)