hdu4975 A simple Gaussian elimination problem.正确解法 最大流+删边判环

本文介绍了一道网络流加判环的经典算法题解,通过分析矩阵行和、列和来判断解的情况,并给出了详细的代码实现及解释。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:有n行m列矩阵,给出各行的和、各列的和,矩阵元素需要为0~9,判断无解、唯一解、多解。

题解:网络流+判环。

和hdu4888几乎一模一样,hdu4888题解链接:http://www.cnblogs.com/yuiffy/p/3891639.html

这题的做法就是hdu4888的做法,点上面的链接怒看如何建图,我下面说这题的不同之处。

可以想到,如果选择一条边递归,然后从递归里出来了,说明没找到环,那么走这条边肯定找不到环,以后也不用走了,可以把这条边删了。或者这条边流量为0,也可以删了。

这样的话每条边最多只进一次,O(m)。

其实4888也可以用这种方法,不过4888还要输出结果,所以先记录结果再删。


//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#include<stack>
#include<queue>
using namespace std;
#define ll long long
#define usll unsigned ll
#define mz(array) memset(array, 0, sizeof(array))
#define minf(array) memset(array, 0x3f, sizeof(array))
#define REP(i,n) for(i=0;i<(n);i++)
#define FOR(i,x,n) for(i=(x);i<=(n);i++)
#define RD(x) scanf("%d",&x)
#define RD2(x,y) scanf("%d%d",&x,&y)
#define RD3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define WN(x) prllf("%d\n",x);
#define RE  freopen("D.in","r",stdin)
#define WE  freopen("1biao.out","w",stdout)
#define mp make_pair
#define pb push_back

int ans;
int r[511],co[511];
int k,nr,nc;
int sumr,sumc;
const int maxn=1111;//点数
const int maxm=555555;//边数
const int inf=9;//MAXINT
struct vnode {
    int v,next;
    int cap;
};
int cnt,head[maxn];
int h[maxn],g[maxn],d[maxn];//g[i]为标号为i的结点个数,h[i]为i结点的标号,d[]当前弧优化,记录当前弧
bool found;
int n,m,st,ed;//n个点m条边
int augc,flow;//augc为增广路容量,flow为最大流
vnode e[maxm];

inline void add(const int &x,const int &y,const int &z) {
    e[cnt].v=y;
    e[cnt].cap=z;
    e[cnt].next=head[x];
    head[x]=cnt;
    cnt++;

    e[cnt].v=x;
    e[cnt].cap=0;
    e[cnt].next=head[y];
    head[y]=cnt;
    cnt++;

}


bool walked[maxn];
bool dfs(const int &x,const int &prex) {///深搜判环
    int biu=-1;
    walked[x]=true;
    for (int i=head[x]; i!=-1; i=e[i].next) {
        if(e[i].v==prex){
            biu=i;
            continue;
        }
        if (e[i].cap>0) {
            if(walked[e[i].v]) return true;
            if(dfs(e[i].v,x)) return true;
        }
        if(biu==-1) head[x]=e[i].next;///删边,因为这条边为0或者走了这条边却没发现环
        else e[biu].next=e[i].next;
        //biu=i;
    }
    walked[x]=false;
    return false;
}

void aug(const int &m) {
    int mini,minh=n-1;
    int augco=augc;
    int &i=d[m];///当前弧优化
    if (m==ed) { //如果当前结点为汇点
        found=true;
        flow+=augc;    //增加流量
        return;
    }
    for (; i!=-1; i=e[i].next) { //寻找容许边
        //printf("m=%d,i=%d,e[i].v=%d,e[i].cap=%d,e[i].next=%d\n",m,i,e[i].v,e[i].cap,e[i].next);
        //getchar();
        if (e[i].cap && h[e[i].v]+1==h[m]) { //如果残留容量大于0,如果是容许边
            if (e[i].cap < augc)  augc=e[i].cap;//如果容许边流量小于当前增广路流量 则更新增广路流量
            //d[m]=i;    //把i定为当前弧
            aug(e[i].v);    //递归
            if (h[st]>=n) return; //GAP 如果源点距离标号大于n 则停止算法
            if (found) break;    //如果找到汇点 则退出寻找
            augc=augco;//没找到就还原当前的流
        }
    }
    if (!found) {      //重标号
        for (int i=head[m]; i!=-1; i=e[i].next) //找那个标号,这里不能用d[m]开始,不然会蛋疼
            if (e[i].cap && h[e[i].v]<minh) {
                minh=h[e[i].v];
                mini=i;
            }
        g[h[m]]--;                                 //GAP 距离为
        if (!g[h[m]]) h[st]=n;                 //GAP
        h[m]=minh+1;
        d[m]=mini;
        g[h[m]]++;                                 //GAP
    } else {
        //修改残量
        e[i].cap-=augc;
        e[i^1].cap+=augc;
    }
}

inline void farm() {
    int i,j,x,y,z;
    memset(head,-1,sizeof(head));
    cnt=0;
    n=nc+nr+2;
    st=n-1;
    ed=n;
    for(i=1; i<=nc; i++)
        add(st,i,co[i]);
    for(i=1; i<=nr; i++)
        add(nc+i,ed,r[i]);
    for(i=1; i<=nc; i++)
        for(j=1; j<=nr; j++)
            add(i,j+nc,k);
    memset(h,0,sizeof(h));
    memset(g,0,sizeof(g));
    g[0]=n;
    flow=0;
    for(i=1; i<=n; i++)
        d[i]=head[i];//当前弧初始化
    while(h[st]<n) {
        augc=inf;//初始化增广路容量为正无穷大
        found=false;
        aug(st);//从源点开始找
    }
    if(flow!=sumr) {
        ans=0;
        return;
    }
    memset(walked,false,sizeof(walked));
    for(i=1; i<=nr; i++) { ///因为建图的特性,有环的话环肯定包含1~nr中的点,也就是表示行的结点
        if(dfs(i,-1)) {
            ans=2;
            return;
        }
    }
    ans=1;
    //printf("%d\n",flow);
}

inline void read(int &a) {///读入优化
    char ch;
    bool flag = false;
    a = 0;
    while(!((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-')));
    if(ch != '-') {
        a *= 10;
        a += ch - '0';
    } else {
        flag = true;
    }
    while(((ch = getchar()) >= '0') && (ch <= '9')) {
        a *= 10;
        a += ch - '0';
    }
    if(flag) {
        a = -a;
    }
}

int main() {
    //RE;
    //WE;
    int i,j,cas=1,t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&nr,&nc);
        k=9;
        sumr=0;
        sumc=0;
        for(i=1; i<=nr; i++) {
            //scanf("%d",&r[i]);
            read(r[i]);
            sumr+=r[i];
        }
        for(i=1; i<=nc; i++) {
            //scanf("%d",&co[i]);
            read(co[i]);
            sumc+=co[i];
        }
        ans=0;
        if(sumr==sumc)farm();
        if(ans==0) printf("Case #%d: So naive!\n",cas++);
        else if(ans!=1) {
            printf("Case #%d: So young!\n",cas++);
        } else {
            printf("Case #%d: So simple!\n",cas++);
        }
    }
    //cout<<"end";
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值