BZOJ2127: happiness
2127: happiness
Time Limit: 51 Sec Memory Limit: 259 MBSubmit: 605 Solved: 283
[Submit][Status]
Description
高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。
Input
第一行两个正整数n,m。接下来是六个矩阵第一个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择文科获得的喜悦值。第二个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学选择理科获得的喜悦值。第三个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择文科获得的额外喜悦值。第四个矩阵为n-1行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i+1行第j列的同学同时选择理科获得的额外喜悦值。第五个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择文科获得的额外喜悦值。第六个矩阵为n行m-1列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学与第i行第j+1列的同学同时选择理科获得的额外喜悦值。
Output
输出一个整数,表示喜悦值总和的最大值
Sample Input
1 1
100 110
1
1000
Sample Output
【样例说明】
两人都选理,则获得100+110+1000的喜悦值。
【数据规模】
对于100%以内的数据,n,m<=100 所有喜悦值均为小于等于5000的非负整数
HINT
Source
题解:
就算是我懂了。。。
搬运题解:
首先发现题目出现了很明显的二分图关系--选文科还是理科,很明显是一个网络流问题,联系到题目上的一个人选文科还是理科可以得到一定喜悦值,两个人同时选文科还是理科,又可以得到一定喜悦值,发现最终答案是由每个人选文科,还是选理科,这个行为确定的,与最小割有一定关系。
继续观察,如果按最小割建立模型,每个人选文选理的代价可以在与s,t的连边上体现,这时难点就变成了如何体现两个人之间的关系:同时选文或选理需要的代价。
这时引入一个很神奇的东西,无向边。
这个东西是我在clj的ppt上看见的,有一个很有用的作用:若A<-->B:C,即A与B连一条权值为C的无向边,当这条无向边计入最小割时,表示这两个点分属于s,t两个集合。
但是仅仅知道如此是不够的,因为对于A,B两点,如果他们都属于S集合,即与T集合的边已经计入最小割了,那A与B之间的边就一定不会出现在最小割里,这一点可以作图验证一二。
所以我们应该对原图进行一定变换。注意到这里我们是想将两个点之间的关系体现在同时取s集或同时取t集的情况,即与s,与t的连接边:若A与B同时选文得到W1的喜悦值,同时选理得到W2的喜悦值,S->A:w1/2,A->T:w2/2,
S->B:w1/2,B->T:w2/2,A<-->B:(w1+w2)/2。
为什么呢?自己画图验证,很神奇的东西,特别难想,不过做过一遍以后再做就觉得很容易了。
似乎从最小割的含义来考虑是显然的?毕竟我还太弱。。。
代码:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #include<set> 10 #include<queue> 11 #include<string> 12 #define inf 1000000000 13 #define maxn 100000 14 #define maxm 500000 15 #define eps 1e-10 16 #define ll long long 17 #define pa pair<int,int> 18 #define rep(x,y) for(int i=1;i<=x;i++)for(int j=1;j<=y;j++) 19 #define FOR for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 20 #define mod 1000000007 21 using namespace std; 22 inline int read() 23 { 24 int x=0,f=1;char ch=getchar(); 25 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 26 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 27 return x*f; 28 } 29 int n,m,s,t,maxflow,cnt=1,tot,a[105][105],b[105][105],mark[105][105],head[maxn],cur[maxn],h[maxn],q[maxn]; 30 struct edge{int go,next,v;}e[maxm]; 31 void ins(int x,int y,int z){e[++cnt].go=y;e[cnt].v=z;e[cnt].next=head[x];head[x]=cnt;} 32 void insert(int x,int y,int z){ins(x,y,z);ins(y,x,0);} 33 void ins2(int x,int y,int z){insert(x,y,z);insert(y,x,z);} 34 bool bfs() 35 { 36 for(int i=s;i<=t;i++)h[i]=-1; 37 int l=0,r=1;q[1]=s;h[s]=0; 38 while(l<r) 39 { 40 int x=q[++l]; 41 for(int i=head[x];i;i=e[i].next) 42 if(e[i].v&&h[e[i].go]==-1) 43 { 44 h[e[i].go]=h[x]+1;q[++r]=e[i].go; 45 } 46 } 47 return h[t]!=-1; 48 } 49 int dfs(int x,int f) 50 { 51 if(x==t) return f; 52 int tmp,used=0; 53 for(int i=head[x];i;i=e[i].next) 54 if(e[i].v&&h[e[i].go]==h[x]+1) 55 { 56 tmp=dfs(e[i].go,min(e[i].v,f-used)); 57 e[i].v-=tmp;if(e[i].v)cur[x]=i; 58 e[i^1].v+=tmp;used+=tmp; 59 if(used==f)return f; 60 } 61 if(!used) h[x]=-1; 62 return used; 63 } 64 void dinic() 65 { 66 maxflow=0; 67 while(bfs()) 68 { 69 for (int i=s;i<=t;i++)cur[i]=head[i];maxflow+=dfs(s,inf); 70 } 71 } 72 int main() 73 { 74 freopen("input.txt","r",stdin); 75 freopen("output.txt","w",stdout); 76 n=read();m=read(); 77 FOR a[i][j]=read(),tot+=a[i][j],a[i][j]<<=1; 78 FOR b[i][j]=read(),tot+=b[i][j],b[i][j]<<=1; 79 FOR mark[i][j]=(i-1)*m+j; 80 s=0;t=mark[n][m]+1; 81 int x; 82 rep(n-1,m) 83 { 84 x=read();tot+=x; 85 a[i][j]+=x;a[i+1][j]+=x; 86 ins2(mark[i][j],mark[i+1][j],x); 87 } 88 rep(n-1,m) 89 { 90 x=read();tot+=x; 91 b[i][j]+=x;b[i+1][j]+=x; 92 ins2(mark[i][j],mark[i+1][j],x); 93 } 94 rep(n,m-1) 95 { 96 x=read();tot+=x; 97 a[i][j]+=x;a[i][j+1]+=x; 98 ins2(mark[i][j],mark[i][j+1],x); 99 } 100 rep(n,m-1) 101 { 102 x=read();tot+=x; 103 b[i][j]+=x;b[i][j+1]+=x; 104 ins2(mark[i][j],mark[i][j+1],x); 105 } 106 FOR{ 107 insert(s,mark[i][j],a[i][j]); 108 insert(mark[i][j],t,b[i][j]); 109 } 110 dinic(); 111 printf("%d\n",tot-(maxflow>>1)); 112 return 0; 113 }
UPD:
写了下另一种解法:
考虑同桌同选文科的额外收获w,
我们新建一个节点x,连边s-> 容量为w,
x到u,v容量为inf。
当w算入割的时候u,v都属于t割,即选了文科。
这种方法新建了很多新点,虽然速度慢,但是好理解。
代码:
1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring> 8 9 #include<algorithm> 10 11 #include<iostream> 12 13 #include<vector> 14 15 #include<map> 16 17 #include<set> 18 19 #include<queue> 20 21 #include<string> 22 23 #define inf 1000000000 24 25 #define maxn 200000+5 26 27 #define maxm 500000+5 28 29 #define eps 1e-10 30 31 #define ll long long 32 33 #define pa pair<int,int> 34 35 #define for0(i,n) for(int i=0;i<=(n);i++) 36 37 #define for1(i,n) for(int i=1;i<=(n);i++) 38 39 #define for2(i,x,y) for(int i=(x);i<=(y);i++) 40 41 #define for3(i,x,y) for(int i=(x);i>=(y);i--) 42 43 #define for4(i,x) for(int i=head[x],y;i;i=e[i].next) 44 #define for5(n,m) for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 45 46 #define mod 1000000007 47 48 using namespace std; 49 int n,m,s,t,cnt,sum,maxflow,tot=1,head[maxn],cur[maxn],h[maxn],num[150][150]; 50 queue<int>q; 51 struct edge{int go,next,v;}e[maxm]; 52 53 inline int read() 54 55 { 56 57 int x=0,f=1;char ch=getchar(); 58 59 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 60 61 while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();} 62 sum+=x*f; 63 64 return x*f; 65 66 } 67 inline void add(int x,int y,int v) 68 { 69 e[++tot]=(edge){y,head[x],v};head[x]=tot; 70 e[++tot]=(edge){x,head[y],0};head[y]=tot; 71 } 72 bool bfs() 73 { 74 for(int i=s;i<=t;i++)h[i]=-1; 75 q.push(s);h[s]=0; 76 while(!q.empty()) 77 { 78 int x=q.front();q.pop(); 79 for(int i=head[x];i;i=e[i].next) 80 if(e[i].v&&h[e[i].go]==-1) 81 { 82 h[e[i].go]=h[x]+1;q.push(e[i].go); 83 } 84 } 85 return h[t]!=-1; 86 } 87 int dfs(int x,int f) 88 { 89 if(x==t) return f; 90 int tmp,used=0; 91 for(int i=cur[x];i;i=e[i].next) 92 if(e[i].v&&h[e[i].go]==h[x]+1) 93 { 94 tmp=dfs(e[i].go,min(e[i].v,f-used)); 95 e[i].v-=tmp;if(e[i].v)cur[x]=i; 96 e[i^1].v+=tmp;used+=tmp; 97 if(used==f)return f; 98 } 99 if(!used) h[x]=-1; 100 return used; 101 } 102 void dinic() 103 { 104 maxflow=0; 105 while(bfs()) 106 { 107 for (int i=s;i<=t;i++)cur[i]=head[i];maxflow+=dfs(s,inf); 108 } 109 } 110 111 int main() 112 113 { 114 115 freopen("input.txt","r",stdin); 116 117 freopen("output.txt","w",stdout); 118 119 n=read();m=read();sum=0;s=0;t=5*n*m+1; 120 for5(n,m)num[i][j]=(i-1)*m+j;cnt=num[n][m]; 121 for5(n,m)add(s,num[i][j],read()); 122 for5(n,m)add(num[i][j],t,read()); 123 for5(n-1,m) 124 { 125 add(s,++cnt,read()); 126 add(cnt,num[i][j],inf); 127 add(cnt,num[i+1][j],inf); 128 } 129 for5(n-1,m) 130 { 131 add(++cnt,t,read()); 132 add(num[i][j],cnt,inf); 133 add(num[i+1][j],cnt,inf); 134 } 135 for5(n,m-1) 136 { 137 add(s,++cnt,read()); 138 add(cnt,num[i][j],inf); 139 add(cnt,num[i][j+1],inf); 140 } 141 for5(n,m-1) 142 { 143 add(++cnt,t,read()); 144 add(num[i][j],cnt,inf); 145 add(num[i][j+1],cnt,inf); 146 } 147 dinic(); 148 printf("%d\n",sum-maxflow); 149 150 return 0; 151 152 }