poj 2396 Budget 解题报告

来源:互联网 发布:守望先锋英雄数据库 编辑:程序博客网 时间:2024/06/02 05:08

Description

We are supposed to make a budget proposal for this multi-site competition. The budget proposal is a matrix where the rows represent different kinds of expenses and the columns represent different sites. We had a meeting about this, some time ago where we discussed the sums over different kinds of expenses and sums over different sites. There was also some talk about special constraints: someone mentioned that Computer Center would need at least 2000K Rials for food and someone from Sharif Authorities argued they wouldn't use more than 30000K Rials for T-shirts. Anyway, we are sure there was more; we will go and try to find some notes from that meeting.

And, by the way, no one really reads budget proposals anyway, so we'll just have to make sure that it sums up properly and meets all constraints.

Input

The first line of the input contains an integer N, giving the number of test cases. The next line is empty, then, test cases follow: The first line of each test case contains two integers, m and n, giving the number of rows and columns (m <= 200, n <= 20). The second line contains m integers, giving the row sums of the matrix. The third line contains n integers, giving the column sums of the matrix. The fourth line contains an integer c (c < 1000) giving the number of constraints. The next c lines contain the constraints. There is an empty line after each test case.

Each constraint consists of two integers r and q, specifying some entry (or entries) in the matrix (the upper left corner is 1 1 and 0 is interpreted as "ALL", i.e. 4 0 means all entries on the fourth row and 0 0 means the entire matrix), one element from the set {<, =, >} and one integer v, with the obvious interpretation. For instance, the constraint 1 2 > 5 means that the cell in the 1st row and 2nd column must have an entry strictly greater than 5, and the constraint 4 0 = 3 means that all elements in the fourth row should be equal to 3.

Output

For each case output a matrix of non-negative integers meeting the above constraints or the string "IMPOSSIBLE" if no legal solution exists. Putone empty line between matrices.

Sample Input

22 3 8 10 5 6 7 4 0 2 > 2 2 1 = 3 2 3 > 2 2 3 < 5 2 2 4 5 6 7 1 1 1 > 10

Sample Output

2 3 3 3 3 4 IMPOSSIBLE 
有上下界的网络流,上下界的网络流一般解法思路如下:

http://blog.csdn.net/water_glass/article/details/6823741

dalao已经讲的很详细了,我主要说说这道题的思路以及代码实现


题意:

acm筹委会需要筹备一些物资,然后每个赛区有固定经费,每个赛区都会有一些物资,而这些物资又会有个固定经费,然后筹委会又给出了一些条件:比如某赛区的衣服价格不得超过1000块之类的,然后问你能不能给出一个合理的经费分派方案满足所有条件。


解题报告

其实经过仔细思考后会发现,在满足那些筹委会的特殊条件时当满足每个赛区的固定经费又满足每个物资的固定经费时就是解了(我们假设一个是行,一个是列),因此我们可以设计网络流的模型了:

设计一个超级源点,其流向每行的流量上下界都为这一行的经费

设计一个超级汇点,每一列流向它的流量上下界都为这一列的经费

再通过给出的那些特殊条件限制一下某些行列的流量上下界

如果这里源点的流出量等于汇点的流入量时其实就相当于满足了行经费之和等于列经费之和,就相当于有解了,否则无解


代码如下:

#include <stdio.h>#include <string.h>#include <queue>#include <algorithm>#include <iostream>#define maxn 305#define inf 100000007using namespace std;int low[maxn][maxn],high[maxn][maxn],flow[maxn][maxn],G[maxn][maxn];//low代表流量下界,high代表流量上届,flow代表最终每条边上的流量,G代表你建的网络流图int in[maxn],out[maxn],row[maxn],col[maxn],layer[maxn],n,m,E;//in代表每个点的流入量,out代表每个点的流出量,row代表每行的经费(流量),low代表列的经费//layer是网络流的分层,E为超级汇点void init(){    E=n+m+1;    memset(low,0,sizeof(low));    memset(high,0,sizeof(high));    memset(flow,0,sizeof(flow));    memset(G,0,sizeof(G));    memset(in,0,sizeof(in));    memset(out,0,sizeof(out));    for(int i=1;i<=n;i++){        for(int j=1;j<=m;j++){            high[i][j+n]=inf;//这里的意思是从第i行流向第j列的流量上限是无穷大        }    }}bool find_layer(int s,int e){//网络流分层    queue<int>q;    while(!q.empty())q.pop();    memset(layer,-1,sizeof(layer));    layer[s]=0;    q.push(s);    while(!q.empty()){        int now=q.front();        q.pop();        for(int i=0;i<=e;i++){//从0到e就能保证遍历图的每一个点了            if(G[now][i] && layer[i]==-1){                layer[i]=layer[now]+1;                q.push(i);                if(i==e)return true;            }        }    }    return false;}int dinic(int s,int e){//dinic网络流算法    deque<int>dq;    while(!dq.empty())dq.pop_back();    int vis[maxn];    int ans=0;    while(find_layer(s,e)){        memset(vis,0,sizeof(vis));        vis[s]=1;        dq.push_back(s);        while(!dq.empty()){            if(dq.back()==e){                int Min=inf,Min_s;                for(int i=1;i<dq.size();i++){                    int temps=dq[i-1],tempe=dq[i];                    if(G[temps][tempe]<Min){                        Min=G[temps][tempe];                        Min_s=temps;                    }                }                ans+=Min;                for(int i=1;i<dq.size();i++){                    int temps=dq[i-1],tempe=dq[i];                    G[temps][tempe]-=Min;                    G[tempe][temps]+=Min;                    flow[temps][tempe]+=Min;                    flow[tempe][temps]-=Min;                }                while(!dq.empty() && dq.back()!=Min_s){                    vis[dq.back()]=0;                    dq.pop_back();                }            }            else{                int flag=1;                for(int i=0;i<=e;i++){                    if(G[dq.back()][i] && !vis[i] && layer[i]==layer[dq.back()]+1){                        vis[i]=1;                        dq.push_back(i);                        flag=0;                        break;                    }                }                if(flag)dq.pop_back();            }        }    }    return ans;}void solve(){    int ss=E+1,ee=E+2,Count=0;//设置附加网络的源和汇    for(int i=0;i<=E;i++){        for(int j=0;j<=E;j++){            G[i][j]=high[i][j]-low[i][j];            in[j]+=low[i][j];            out[i]+=low[i][j];            Count+=low[i][j];//统计所有点的总流量下界        }    }    for(int i=0;i<=E;i++)G[ss][i]=in[i];//构建附加网络    for(int i=0;i<=E;i++)G[i][ee]=out[i];    G[E][0]=inf;    int Min_flow=dinic(ss,ee);//计算最小流    if(Min_flow!=Count){//如果最小流不等于Count,则没有答案        cout<<"IMPOSSIBLE"<<endl;        return ;    }    G[E][0]=0;    dinic(0,E);//在原图上再跑一边   //马丹想了好久一直想不通为什么还要再跑一遍,现在终于想明白了,根本特么不用再跑一遍,因为入的上下界相等,出的上下界相等,所以这道题根本不用再跑!!! //这里注意,此时ss与ee都是大于E的,因此没必要取消边,因为你这次根本遍历不到它    for(int i=1;i<=n;i++){        cout<<low[i][1+n]+flow[i][1+n];//注意末尾不能有空格        for(int j=2;j<=m;j++) printf(" %d",low[i][j+n]+flow[i][j+n]);        printf("\n");    }}int main(){    int test;    scanf("%d",&test);    while(test--){        scanf("%d%d",&n,&m);        init();        for(int i=1;i<=n;i++)scanf("%d",&row[i]);        for(int i=1;i<=m;i++)scanf("%d",&col[i]);        int k;        scanf("%d",&k);        while(k--){            int x,y,v;            char s[10];            cin>>x>>y>>s>>v;            //注意某条流量可能被限定多次,因此用max或min取极值            if(x){                if(y){                    if(s[0]=='=')low[x][y+n]=high[x][y+n]=v;                    else if(s[0]=='<')high[x][y+n]=min(v-1,high[x][y+n]);                    else low[x][y+n]=max(v+1,low[x][y+n]);                }                else {                    for(int i=1;i<=m;i++){                        if(s[0]=='=')low[x][i+n]=high[x][i+n]=v;                        else if(s[0]=='<')high[x][i+n]=min(v-1,high[x][i+n]);                        else low[x][i+n]=max(v+1,low[x][i+n]);                    }                }            }            else {                if(y){                    for(int i=1;i<=n;i++){                        if(s[0]=='=')low[i][y+n]=high[i][y+n]=v;                        else if(s[0]=='<')high[i][y+n]=min(v-1,high[i][y+n]);                        else low[i][y+n]=max(v+1,low[i][y+n]);                    }                }                else{                    for(int i=1;i<=n;i++){                        for(int j=1;j<=m;j++){                            if(s[0]=='=')low[i][j+n]=high[i][j+n]=v;                            else if(s[0]=='<')high[i][j+n]=min(v-1,high[i][j+n]);                            else low[i][j+n]=max(v+1,low[i][j+n]);                        }                    }                }            }        }        //限定流量上下界        for(int i=1;i<=n;i++)low[0][i]=high[0][i]=row[i];        for(int i=1;i<=m;i++)low[i+n][E]=high[i+n][E]=col[i];        solve();    }}