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;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章