网络流: HDU 3338

本文解析了HDU3338 Kakuro Extension问题,介绍了一种利用网络流算法解决数独变体Kakuro的方法。通过调整数值范围、构造网络流模型并运行最大流算法,成功解决了题目要求的数独谜题。

HDU 3338

hdu 3338
Kakuro Extension
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2809 Accepted Submission(s): 971
Special Judge

Problem Description
If you solved problem like this, forget it.Because you need to use a completely different algorithm to solve the following one.
Kakuro puzzle is played on a grid of “black” and “white” cells. Apart from the top row and leftmost column which are entirely black, the grid has some amount of white cells which form “runs” and some amount of black cells. “Run” is a vertical or horizontal maximal one-lined block of adjacent white cells. Each row and column of the puzzle can contain more than one “run”. Every white cell belongs to exactly two runs — one horizontal and one vertical run. Each horizontal “run” always has a number in the black half-cell to its immediate left, and each vertical “run” always has a number in the black half-cell immediately above it. These numbers are located in “black” cells and are called “clues”.The rules of the puzzle are simple:

1.place a single digit from 1 to 9 in each “white” cell
2.for all runs, the sum of all digits in a “run” must match the clue associated with the “run”

Given the grid, your task is to find a solution for the puzzle.

Sample Input

6 6
XXXXXXX XXXXXXX 028\XXX 017\XXX 028\XXX XXXXXXX
XXXXXXX 022\022 ....... ....... ....... 010\XXX
XXX\034 ....... ....... ....... ....... .......
XXX\014 ....... ....... 016\013 ....... .......
XXX\022 ....... ....... ....... ....... XXXXXXX
XXXXXXX XXX\016 ....... ....... XXXXXXX XXXXXXX
5 8
XXXXXXX 001\XXX 020\XXX 027\XXX 021\XXX 028\XXX 014\XXX 024\XXX
XXX\035 ....... ....... ....... ....... ....... ....... .......
XXXXXXX 007\034 ....... ....... ....... ....... ....... .......
XXX\043 ....... ....... ....... ....... ....... ....... .......
XXX\030 ....... ....... ....... ....... ....... ....... XXXXXXX

Sample Output

_ _ _ _ _ _
_ _ 5 8 9 _
_ 7 6 9 8 4
_ 6 8 _ 7 6
_ 9 2 7 4 _
_ _ 7 9 _ _
_ _ _ _ _ _ _ _
_ 1 9 9 1 1 8 6
_ _ 1 7 7 9 1 9
_ 1 3 9 9 9 3 9
_ 6 7 2 4 9 2 _

题意:给你一个n*m的矩阵,然后每个位置有一段长度为7的字符串,根据他对各个字符串的定义,会有一些要填入的总数和,有些是要计算出的总数和,有些是要你填的位置。他要求你填入一些数,使行和列的值的总和满足他给你要求的值,就是图中白色区域的行列值的和和黑色区域要求的值相等。

解题思路:据说这个是个很神奇的网络流,不过我的思路很常规,有一个处理的地方,因为他说填入的数的范围是[1,9],而最大流无法规定下限,所以我们采取减1,取[0,8]的范围,最后再给他加上去就好了。大源点和大汇点,大源点向他提供的“源点”建一条流量为他要求的值的边,他提供的“汇点”向大汇点建一条流量为他要求的值的边。然后就是提供“源点”向要填入的数的点(空白点)建一条流量为8的边,空白点向提供“汇点”建一条流量为8的边。注意因为可能提供“汇点”和提供“源点”是同一个点,所以我们要拆一下点,虽然这个拆了点不是原来拆点的用处,只是为了让他变成两个不同的点,为了敲的方便,所以拆点了。。。。。然后就跑个最大流,再把流量输出就行了。

答案很多的原因:

在这里插入图片描述
对答案是没有影响的

完整代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=205;
const int inf=0x3f3f3f3f;
struct Edge{
    int u,v,c;
    int next;
}edge[maxn*maxn];
int n,m;
int edn;//边数
int head[maxn*maxn],dis[maxn*maxn];
int sp,tp;//原点,汇点

void addedge(int u,int v,int c)
{
    edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
    edge[edn].next=head[u]; head[u]=edn++;

    edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
    edge[edn].next=head[v]; head[v]=edn++;
}
int bfs()
{
    queue <int> q;
    memset(dis,-1,sizeof(dis));
    dis[sp]=0;
    q.push(sp);
    while(!q.empty())
    {
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=edge[i].next)
        {
            int u=edge[i].v;
            if(dis[u]==-1 && edge[i].c>0)
            {
                dis[u]=dis[cur]+1;
                q.push(u);
            }
        }
    }
    return dis[tp] != -1;
}
int dfs(int a,int b)
{
    int r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=edge[i].next)
    {
        int u=edge[i].v;
        if(edge[i].c>0 && dis[u]==dis[a]+1)
        {
            int x=min(edge[i].c,b-r);
            x=dfs(u,x);
            r+=x;
            edge[i].c-=x;
            edge[i^1].c+=x;
        }
    }
    if(!r)dis[a]=-2;
    return r;
}

int dinic(int sp,int tp)
{
    int total=0,t;
    while(bfs())
    {
        while(t=dfs(sp,inf))
        total+=t;
    }
    return total;
}
int mp[105][105][3];
int main()
{
    int i,u,v,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        edn=0;//初始化
        memset(head,-1,sizeof(head));
        memset(mp,0,sizeof(mp));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                char q[10];
                scanf("%s",q);
                if(q[3]=='X')
                {
                    mp[i][j][0]=-1;
                    continue;
                }else{
                    if(q[3]=='.')
                    {
                        mp[i][j][0]=1;
                        continue;
                    }
                    if(q[0]!='X')
                    {
                        mp[i][j][2]=(q[0]-'0')*100+(q[1]-'0')*10+(q[2]-'0');
                    }
                    if(q[4]!='X')
                    {
                        mp[i][j][1]=(q[4]-'0')*100+(q[5]-'0')*10+q[6]-'0';
                    }

                }
            }
        }
        sp=0,tp=n*m*2+1;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(mp[i][j][1]!=0)
                {

                    int k;
                    for(k=j+1;k<m;k++)
                    {
                        if(mp[i][k][0]==1)
                        {
                             addedge(i*m+j+1,i*m+k+1,8);//提供“源点”向空白点建边
                        }else{
                            break;
                        }
                    }
                    addedge(sp,i*m+j+1,mp[i][j][1]-(k-1-j));//大源点向提供“源点”建边
                }
                if(mp[i][j][2]!=0)
                {
                    int k;
                    for(k=i+1;k<n;k++)
                    {
                        if(mp[k][j][0]==1)
                        {
                            addedge(k*m+j+1,n*m+i*m+j+1,8);//空白点向提供“汇点”建边
                        }else
                            break;
                    }
                    addedge(n*m+i*m+j+1,tp,mp[i][j][2]-(k-1-i));//提供“汇点”向大汇点建边
                }
            }
        }
        int ans=dinic(sp,tp);
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m-1;j++)
            {
                if(mp[i][j][0]==1)
                {
                    for(int u=head[i*m+j+1];u!=-1;u=edge[u].next)
                    {
                        printf("%d ",edge[u].c+1);
                        break;
                    }
                }else{
                    printf("_ ");
                }
            }
            if(mp[i][m-1][0]==1)
            {
                    for(int u=head[i*m+m-1+1];u!=-1;u=edge[u].next)
                    {
                        printf("%d\n",edge[u].c+1);
                        break;
                    }
            }else{
                    printf("_\n");
            }
        }
    }


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值