HDU3338 Kakuro Extension(最大流)

给出一个大小为 nm(2n,m100) 的数谜,在每个白格子里面填上1~9的数字,使得横向的和等于黑格子右上角的数,竖向的和等于黑格子左下角的数。
我的第一反应,搜索!
但是n,m有100那么大,估计也搜不过。
于是搜了搜题解,才知道网络流可以这样建模:
源点s连向黑格子右上角,容量为黑格子右上角的数,表示需要得到的和;黑格子的右上角分别向它对应的白格子连边,容量为9且流量必须大于等于1,为了方便,我们把容量-1,这样容量变成了8,s连出去的边的容量也要相应的减少对应的白格子数量,输出的时候再加回来,流量的含义为需要填的数;每一列的白格子分别连向它们对应的黑格子左下角,容量均为8,理由同上;最后,每个黑格子左下角再连向汇点,容量为左下角的数减去对应的白格子数,表示需要得到的和。
这样一来,求出的最大流后的残留网络就应该是数谜的一组解,只需要统计连向白格子的边的流量。
很巧妙,对不对!
样例建出来的图大概是这样的,渣渣手绘,请见谅:
样例输入
样例建出来的图
其中浅蓝色的点是黑格子右上角,深蓝色的点是黑格子左下角(在图中貌似很模糊- -),红点是白格子。不难发现,每条边的容量都减去了相应的值。

#include<cstdio>
#include<cstring>
#include<vector>
#define MAXN 40010
using namespace std;
inline int Min(int a,int b)
{return a<b?a:b;}
struct E
{
    int v,w,op;
    E(){}
    E(int a,int b,int c)
    {v = a; w = b; op = c;}
};
vector<E> g[MAXN];
int d[MAXN],vd[MAXN],n,m,col[110][110],row[110][110],ans[110][110];
int posrow[MAXN],srow[MAXN],cntx,poscol[MAXN],scol[MAXN],cnty,flow;
int s,t;
char str[10];
void init()
{
    for(int i = 1; i <= t; i++) g[i].clear();
    memset(posrow,0,sizeof posrow);
    memset(srow,0,sizeof srow);
    memset(poscol,0,sizeof poscol);
    memset(scol,0,sizeof scol);
    memset(col,0,sizeof col);
    memset(row,0,sizeof row);
    memset(ans,0,sizeof ans);
    cntx = cnty = s = t = 0;
}
void get_num(int i,int j)
{
    scanf("%s",str);
    if(str[0] == 'X') col[i][j] = -2;
    else if(str[0] == '.') col[i][j] = -1;
    else col[i][j] = (str[0]-'0')*100+(str[1]-'0')*10+str[2]-'0';

    if(str[4] == 'X') row[i][j] = -2;
    else if(str[4] == '.') row[i][j] = -1;
    else row[i][j] = (str[4]-'0')*100+(str[5]-'0')*10+str[6]-'0';
}
inline int index(int i,int j)//点标号
{
    return (i-1)*m+j;
}
inline void back(int dex,int &i,int &j)
{
    i = (dex-1)/m+1;
    j = dex%m;
    if(j == 0) j = m;
}
void build()//输入好恶心- -
{
    int tx,p,nS;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            if(row[i][j]>=0)
            {   
                tx = index(i,j);
                posrow[++cntx] = tx;
                srow[cntx] = row[i][j];
            }
            else if(row[i][j] == -1)
            {
                srow[cntx]--;//统计对应白格子的数,减去它们
                p = index(i,j);
                g[tx].push_back(E(p,8,g[p].size()));
                g[p].push_back(E(tx,0,g[tx].size()-1));
            }
        }
    s = m*n+1;
    nS = s;
    for(int i = 1; i <= cntx; i++)//源点s连边
    {
        g[s].push_back(E(posrow[i],srow[i],g[posrow[i]].size()));
        g[posrow[i]].push_back(E(s,0,g[s].size()-1));
    }
    for(int j = 1; j <= m; j++)
        for(int i = 1; i <= n; i++)
        {
            if(col[i][j] >= 0)
            {
                poscol[++cnty] = ++nS;
                scol[cnty] = col[i][j];
            }
            else if(col[i][j] == -1)
            {
                scol[cnty]--;
                p = index(i,j);
                g[p].push_back(E(poscol[cnty],8,g[poscol[cnty]].size()));
                g[poscol[cnty]].push_back(E(p,0,g[p].size()-1));
            }
        }
    t = nS+1;
    for(int i = 1; i <= cnty; i++)//向汇点t连边
    {
        g[poscol[i]].push_back(E(t,scol[i],g[t].size()));
        g[t].push_back(E(poscol[i],0,g[poscol[i]].size()-1));
    }
}
int aug(int i,int augco)
{
    int j,augc = augco,mind = t-1,delta,sz = g[i].size();
    if(i == t) return augco;

    for(j = 0; j < sz; j++)
    {
        int v = g[i][j].v;
        if(g[i][j].w)
        {
            if(d[i] == d[v]+1)
            {
                delta = Min(g[i][j].w,augc);
                delta = aug(v,delta);
                g[i][j].w -= delta;
                g[v][g[i][j].op].w += delta;
                augc -= delta;
                if(d[s] >= t) return augco - augc;
                if(augc == 0) break;
            }
            if(d[v] < mind) mind = d[v];
        }
    }
    if(augc == augco)
    {
        vd[d[i]]--;
        if(vd[d[i]] == 0) d[s] = t;
        d[i] = mind+1;
        vd[d[i]]++;
    }
    return augco - augc;
}
void sap()
{
    memset(d,0,sizeof d);
    memset(vd,0,sizeof vd);
    flow = 0;
    vd[0] = t;
    while(d[s] < t)
        flow += aug(s,0x3f3f3f3f);
}
void solution()
{
    int v,ssz = g[s].size(),vsz,to,i,j,x,y;
    for(i = 0; i < ssz; i++)
    {
        v = g[s][i].v;
        vsz = g[v].size();
        for(j = 0; j < vsz; j++)
        {
            to = g[v][j].v;
            back(to,x,y);//求出该编号对应的点
            ans[x][y] = 9-g[v][j].w;
        }
    }
}
int main()
{
    while(scanf("%d%d",&n,&m) != EOF)
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                get_num(i,j);

        build();//建图

        sap();//网络流

        solution();

        for(int i = 1; i <= n; i++)
        {   
            for(int j = 1; j <= m; j++)
                printf("%c%c",ans[i][j]==0?'_':ans[i][j]+'0',j==m?'\n':' ');
            printf("\n");
        }       
        init();
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值