HDU3338Kakuro Extension(最大流,ISAP)建图是关键

来源:互联网 发布:java中布尔函数编程 编辑:程序博客网 时间:2024/06/05 23:53

Kakuro Extension

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1144    Accepted Submission(s): 404
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.
              
        Picture of the first sample input            Picture of the first sample output
 

Input
The first line of input contains two integers n and m (2 ≤ n,m ≤ 100) — the number of rows and columns correspondingly. Each of the next n lines contains descriptions of m cells. Each cell description is one of the following 7-character strings:

.......— "white" cell;
XXXXXXX— "black" cell with no clues;
AAA\BBB— "black" cell with one or two clues. AAA is either a 3-digit clue for the corresponding vertical run, or XXX if there is no associated vertical run. BBB is either a 3-digit clue for the corresponding horizontal run, or XXX if there is no associated horizontal run.
The first row and the first column of the grid will never have any white cells. The given grid will have at least one "white" cell.It is guaranteed that the given puzzle has at least one solution.
 

Output
Print n lines to the output with m cells in each line. For every "black" cell print '_' (underscore), for every "white" cell print the corresponding digit from the solution. Delimit cells with a single space, so that each row consists of 2m-1 characters.If there are many solutions, you may output any of them.
 

Sample Input
6 6XXXXXXX XXXXXXX 028\XXX 017\XXX 028\XXX XXXXXXXXXXXXXX 022\022 ....... ....... ....... 010\XXXXXX\034 ....... ....... ....... ....... .......XXX\014 ....... ....... 016\013 ....... .......XXX\022 ....... ....... ....... ....... XXXXXXXXXXXXXX XXX\016 ....... ....... XXXXXXX XXXXXXX5 8XXXXXXX 001\XXX 020\XXX 027\XXX 021\XXX 028\XXX 014\XXX 024\XXXXXX\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 _
 

Author
NotOnlySuccess@HDU
 

Source
HDOJ Monthly Contest – 2010.03.06
 

题意:输入一个n,m,表示表格有n行m列,黑格子里的数子分水平与垂直,如果一个数在 “\” 的右方表示这个数由当前格子的水平右方连续的白格子内的数的总和(白格子内的数需要填写,范围:1 ~ 9),如果一个数在左方,由当前垂直下方连续的白格子内的数的总和。所以每个白格了恰好用了两次,一次水平,一次垂直。给出的表格填写白格子内的数时总是有答案的。填写方式任何一种满足条件即可(白格子内所填的数至少为1)。


解析:一开始看了题后无从下手啊,看了网上的解题报告,用网络流解。关键是理解怎么建图。为什么会想到网络流来解题呢,1:从水平方向看,所有的水平方向白格子内数的总和恰好等于垂直方向上的白格内数的总和,总流入量==总流出量。2:每个白格子恰好用了两次,一次是水平一次是垂直,流入点的边与流出点的边都只有一条边,   量的入出是相等的,也就是白格子内所填的数子。

建图:分下列几类点。

S:超极源点

A ::黑格子内水平方向的数看成一个点。

B  :白格子。

C:黑格子内垂直方向的数看成另一个点。

T:超极汇点

图中的边:

(S , A  , capcity): capcity为边的容量=A点的数  -  水平右方连续白格子的个数(保证了白格子内的数至少为1)

(A , B ,  8):容量为8:保证了白格子内的数至少为1(白格子点的入边)

(B , C , 8):同理(白格子点的出边)

(C ,  T  ,capcity):  capcity为边的容量=C点的数  - 垂直下方连续白格子的个数

#include<stdio.h>#include<string.h>#include<queue>using namespace std;#define captype intconst int MAXN = 100010;   //点的总数const int MAXM = 400010;    //边的总数const int INF = 1<<30;struct EDG{    int to,next;    captype cap,flow;} edg[MAXM];int eid,head[MAXN];int gap[MAXN];  //每种距离(或可认为是高度)点的个数int dis[MAXN];  //每个点到终点eNode 的最短距离int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边void init(){    eid=0;    memset(head,-1,sizeof(head));}//有向边 三个参数,无向边4个参数void addEdg(int u,int v,captype c,captype rc=0){    edg[eid].to=v; edg[eid].next=head[u];    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;    edg[eid].to=u; edg[eid].next=head[v];    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;}//预处理eNode点到所有点的最短距离void BFS(int sNode, int eNode){    queue<int>q;    memset(gap,0,sizeof(gap));    memset(dis,-1,sizeof(dis));    gap[0]=1;    dis[eNode]=0;    q.push(eNode);    while(!q.empty()){        int u=q.front(); q.pop();        for(int i=head[u]; i!=-1; i=edg[i].next){            int v=edg[i].to;            if(dis[v]==-1){                dis[v]=dis[u]+1;                gap[dis[v]]++;                q.push(v);            }        }    }}int node_f[MAXN]; //记实际流过每个点的值,也就是正向流过当前点的值,而非回流int S[MAXN];    //路径栈,存的是边的id号captype maxFlow_sap(int sNode,int eNode, int n){  //注意:n为点的总个数,包括源点与汇点    BFS(sNode, eNode);              //预处理eNode到所有点的最短距离    if(dis[sNode]==-1) return 0;    //源点到不可到达汇点    memcpy(cur,head,sizeof(head));    memset(node_f,0,sizeof(node_f));    int top=0;  //栈顶    captype ans=0;  //最大流    int u=sNode;    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点        if(u==eNode){   //找到一条可增流的路            captype Min=INF ;            int inser;            for(int i=0; i<top; i++)    //从这条可增流的路找到最多可增的流量Min            if(Min>edg[S[i]].cap-edg[S[i]].flow){                Min=edg[S[i]].cap-edg[S[i]].flow;                inser=i;            }            for(int i=0; i<top; i++){                edg[S[i]].flow+=Min;                edg[S[i]^1].flow-=Min;  //可回流的边的流量                if(edg[S[i]].cap>0)                    node_f[edg[S[i]].to]=edg[S[i]].flow;                else                    node_f[edg[S[i]^1].to]=edg[S[i]^1].flow;            }            ans+=Min;            top=inser;  //从这条可增流的路中的流量瓶颈 边的上一条边那里是可以再增流的,所以只从断流量瓶颈 边裁断            u=edg[S[top]^1].to;  //流量瓶颈 边的起始点            continue;        }        bool flag = false;  //判断能否从u点出发可往相邻点流        int v;        for(int i=cur[u]; i!=-1; i=edg[i].next){            v=edg[i].to;            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){                flag=true;                cur[u]=i;                break;            }        }        if(flag){            S[top++] = cur[u];  //加入一条边            u=v;            continue;        }        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1        int Mind= n;        for(int i=head[u]; i!=-1; i=edg[i].next)        if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){            Mind=dis[edg[i].to];            cur[u]=i;        }        gap[dis[u]]--;        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流        dis[u]=Mind+1;      //如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1        gap[dis[u]]++;        if(u!=sNode) u=edg[S[--top]^1].to;  //退一条边    }    return ans;}struct NODE{    char num[10];    int num1,num2;    int id1,id2;    //点的编号}mapt[105][105];int main(){    int n,m;    while(scanf("%d%d",&n,&m)>0){        init();        int k=1;        for(int i=0; i<n; i++)            for(int j=0; j<m; j++){                scanf("%s",mapt[i][j].num);                char ss[10];                strcpy(ss,mapt[i][j].num);                mapt[i][j].num1=-1;                mapt[i][j].num2=-1;                mapt[i][j].id1=-1;                mapt[i][j].id2=-1;                if(ss[0]=='.')                    mapt[i][j].id1=k++;                if(ss[0]>='0'&&ss[0]<='9'){                    mapt[i][j].num1=(ss[0]-'0')*100+(ss[1]-'0')*10+ss[2]-'0';                    mapt[i][j].id1=k++;                }                if(ss[4]>='0'&&ss[4]<='9'){                    mapt[i][j].num2=(ss[4]-'0')*100+(ss[5]-'0')*10+ss[6]-'0';                    mapt[i][j].id2=k++;                }            }        int s=0 , t = k ;        for(int i=0; i<n; i++){            int j=0;            while(j<m){                while(j<m&&mapt[i][j].num2==-1)j++;    //找一个水平方向有值的格子                if(j>=m) break;                int u=mapt[i][j].id2 ,c=mapt[i][j].num2 ,tk=0;                j++;                while(j<m&&mapt[i][j].num[0]=='.'){                    int v=mapt[i][j].id1;                    addEdg(u,v,8);                    j++; tk++;                }                addEdg(s,u,c-tk); //记得要减空格子的个数,因为每个空格子值至少为1            }        }        for(int j=0; j<m; j++){            int i=0;            while(i<n){                while(i<n&&mapt[i][j].num1==-1)i++;                if(i>=n) break;                int v=mapt[i][j].id1 , c=mapt[i][j].num1 ,tk=0;                i++;                while(i<n&&mapt[i][j].num[0]=='.'){                    int u=mapt[i][j].id1;                    addEdg(u,v,8);                    i++; tk++;                }                addEdg(v,t,c-tk);            }        }                maxFlow_sap(s,t,k+1);        for(int i=0; i<n; i++){            int j;            for(j=0; j<m-1; j++)                if(mapt[i][j].num[0]=='.')                    printf("%d ",node_f[mapt[i][j].id1]+1);                else                    printf("_ ");            if(mapt[i][j].num[0]=='.')                printf("%d\n",node_f[mapt[i][j].id1]+1);            else                printf("_\n");        }    }}


0 0