洛谷1418
题目链接P1418 [TJOI2011]构造矩阵
题目大意:在一个n*m(n<=100,m<=100)的矩阵中,每一个位置可以填1或0,给出每行的1的个数,求出字典序最小的矩阵。
题目思路:乍一看这个题好像是一个网络流的题,但是题目中要求考虑字典序,因此肯定不能直接跑网络流,如果搜索的话复杂度又不能接受。因此想到是否可以在每次遍历到某个点时直接确定他的值而不再进行回溯。因为要求字典序尽可能小,因此如果在当前点可以填0就填0,否则填1.判断只需要用网络流来跑。但这样看复杂度变成了n^4,所以要进行优化。我们发现,这次跑的网络流和上一次相比其实只有两条边发生了变化(只有这次的x0,y0,以及上次的x’,y’)。所以每次只需要在残量网络上进行操作,把这个点所确定的边退流,再重新跑一次,如果可行就填0,不可行就填1(还有些细节没有写,请参照代码).最后给出完整代码:
题目代码:
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
#define MXN 200100
#define mxn 210
using namespace std;
int EHead[mxn],ENext[MXN],ECost[MXN],ETo[MXN],EFr[MXN],LT[mxn][mxn];
int EBack[MXN],Back[mxn],Lt,vis[mxn],n,m,pns,R[mxn],C[mxn];
void AddEdge(int fr,int to,int cc){
Lt++;
ECost[Lt]=cc;
ETo[Lt]=to;
EFr[Lt]=fr;
ENext[Lt]=EHead[fr];
EHead[fr]=Lt;
}
void PRE(int fr,int to,int cc){
AddEdge(fr,to,cc);
LT[fr][to]=Lt;
AddEdge(to,fr,0);
LT[to][fr]=Lt;
EBack[Lt]=Lt-1;
EBack[Lt-1]=Lt;
}
bool BFS(){
queue<int>Qe;
while(!Qe.empty())Qe.pop();
Qe.push(1);
vis[1]=1;
while(!Qe.empty()){
int s=Qe.front();Qe.pop();
if(s==2+n+m)return true;
for(int i=EHead[s];i;i=ENext[i]){
if(!vis[ETo[i]]&&ECost[i]>0){
vis[ETo[i]]=1;
Qe.push(ETo[i]);
Back[ETo[i]]=i;
}
}
}
return false;
}
int Flow(){
int wp=2+n+m,ms=0x7fffffff;
while(wp^1){
ms=min(ms,ECost[Back[wp]]);
wp=EFr[Back[wp]];
}
wp=2+n+m;
while(wp^1){
ECost[Back[wp]]-=ms;
ECost[EBack[Back[wp]]]+=ms;
wp=EFr[Back[wp]];
}
return ms;
}
int Dinic(){
int ret=0;
memset(vis,0,sizeof(vis));
while(BFS()){
ret+=Flow();
memset(vis,0,sizeof(vis));
}
return ret;
}
bool Check(int x,int y){
Dinic();
if(!R[x]||!C[y])return false;
R[x]--;C[y]--;
if(ECost[LT[x+1][y+n+1]]){
ECost[LT[x+1][y+n+1]]=0;
return true;
}
ECost[LT[x+1][y+n+1]+1]=0;
ECost[LT[1][x+1]]++;
ECost[LT[y+n+1][n+m+2]]++;
ECost[LT[1][x+1]+1]--;
ECost[LT[y+n+1][n+m+2]+1]--;
if(Dinic()>0)return true;
R[x]++;
C[y]++;
ECost[LT[1][x+1]]--;
ECost[LT[y+n+1][n+m+2]]--;
ECost[LT[1][x+1]+1]++;
ECost[LT[y+n+1][n+m+2]+1]++;
return false;
}
int main()
{
int r,c;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&r);
R[i]=m-r;
PRE(1,i+1,r);
}
for(int i=1;i<=m;i++){
scanf("%d",&c);
C[i]=n-c;
PRE(i+n+1,n+m+2,c);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
PRE(i+1,j+n+1,1);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(!Check(i,j))printf("1");
else printf("0");
}
putchar(10);
}
return 0;
}