HDU 3657 Game

本文介绍了一种使用网络流算法解决特定矩阵选择问题的方法。问题要求从矩阵中选择若干格子以最大化得分,同时考虑格子间的相邻惩罚及必选格子条件。通过构建超级源点与汇点,并根据不同条件设置边的容量与流向,实现最大流最小割算法求解。

作为一个刚入门网络流的弱菜,表示看到这个题,实在建不出图。直接在discuss看的别人怎么建图的。既然看了别人的建图方法,那只能讲讲目前的理解了。

 题意: 给定一个 n * m 大小的矩阵, 矩阵中每个格子都有一个不大于 1000 的正整数, 现在要从矩阵中选出若干个格子, 使得得分最大. 得分等于选取的格子里的数字和. 如果有两个格子相邻, 则得分将减去 2 * (x & y), x 和 y 为相邻两个格子内的数字. 此外, 还有一些格子是必选的.

二分图最小点权覆盖的建图方法:

建立超级源点S和超级汇点T,对于二分图X,Y中的点,从S向X连一条容量为X权值的边,从Y向T连一条容量为Y权值的边,其余边容量无穷大。由S->T做一遍最大流。对于该图中任意一个割,那个割所对应的点就是一个可行解,最小权和为割的容量。

在本题中:建立超级源点S和超级汇点T,对行号i+列号j为奇数的点作为二分图X中的点,为偶数的作为二分图中Y中的点,从S向X连边,从Y向T连边,若该节点必选,则容量为+INF,否则,容量为该带你权值,对于从X到Y中的可以相邻的点(就是在原图中有公共边的格子对应的点),容量设置成2*(x&y)。由S->T做一次最大流,最终答案是所有格子中的点的权值之和sum-Maxflow(S->T)。

看完这个建图方法,我瞬间就冒出很多傻逼的问题。。以下是一些自问自答。。答的不一定对。

1.最大流=最小割,这张图中的最小割对应的就是在原矩阵中损失掉的分数。那么在本题中,就是要使分数流失最小。那如果我让总流量小一点,那不是分数损失的更少吗?对于一条边上分数的损失,要么就损失,要么就不损失,损失的时候就一定达到满流。

2.为什么必选点到源汇点的流量必须是+INF?容量无穷,改变不可能成为最小割的边,也以为着这条边一定上的分数不能损失,也就是必选。那么对其他点,容量有限,就有可能出现在最小割中,从而那条边上的值成为被牺牲掉的分数。

3.X,Y之间的点的流量为什么要设置成2*(x&y)?为了限制损失。如果没有满流,就说明损失这条边上的分数不值得。

4.同上,那为什么在二分图最小点权覆盖问题中,X->Y的边上的容量可以设为+INF?同样把流量看成一种损失。容量设置成INF,意味着不用去权衡这条边上的损失,造成损失的根源不在这条边,而是在从源点S到X中的点的那条边上。

这只是现在的理解。。应该有一些不合理的地方。。说不定我以后回过头来看这篇东西,会觉得好搞笑。。

贴代码:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <queue>
  5 #include <vector>
  6 #include <algorithm>
  7 #define maxn 3000
  8 #define maxm 15000
  9 #define INF 1<<30
 10 using namespace std;
 11 
 12 int n,m,k,S,T,e;
 13 int score[55][55];
 14 bool flag[55][55];
 15 int dx[] = {0,-1,0,1};
 16 int dy[] = {1,0,-1,0};
 17 int first[maxn],v[maxm],w[maxm],next[maxm];
 18 int d[maxn],q[maxn],work[maxn];
 19 
 20 void init(){
 21     memset(first,-1,sizeof(first));
 22     e = 0;
 23 }
 24 
 25 void add_edge(int a,int b,int c){
 26     //printf("addedge %d %d %d\n",a,b,c);
 27     v[e] = b;next[e] = first[a];w[e] = c;first[a] = e++;
 28     v[e] = a;next[e] = first[b];w[e] = 0;first[b] = e++;
 29 }
 30 
 31 int bfs(){
 32     int i,j,rear = 0;
 33     memset(d,-1,sizeof(d));
 34     d[S] = 0;q[rear++] = S;
 35     for(int i = 0;i < rear;i++){
 36         for(int j = first[q[i]];j != -1;j = next[j])
 37             if(w[j] && d[v[j]] == -1){
 38                 d[v[j]] = d[q[i]] + 1;
 39                 q[rear++] = v[j];
 40                 if(v[j] == T)   return 1;
 41             }
 42     }
 43     return 0;
 44 }
 45 
 46 int dfs(int cur,int a){
 47     if(cur == T)    return a;
 48     for(int &i = work[cur];i != -1;i = next[i])
 49         if(w[i] && d[v[i]] == d[cur] + 1)
 50             if(int t = dfs(v[i],min(a,w[i]))){
 51                 w[i] -= t;w[i^1] += t;
 52                 return t;
 53             }
 54     return 0;
 55 }
 56 
 57 int dinic(){
 58     int ans = 0,t;
 59     while(bfs()){
 60         memcpy(work,first,sizeof(first));
 61         while(t = dfs(S,INF))   ans += t;
 62     }
 63     return ans;
 64 }
 65 
 66 int ID(int row,int column){
 67     if(row < 1 || row > n)  return 0;
 68     if(column < 1 || column > m) return 0;
 69     return (row-1) * m + column;
 70 }
 71 
 72 int main()
 73 {
 74     while(scanf("%d%d%d",&n,&m,&k) == 3){
 75         init();
 76         int sum = 0;
 77         S = 0,T = n*m+1;
 78         memset(flag,0,sizeof(flag));
 79         for(int i = 1;i <= n;i++){
 80             for(int j = 1;j <= m;j++){
 81                 scanf("%d",&score[i][j]);
 82                 sum += score[i][j];
 83             }
 84         }
 85         for(int i = 0;i < k;i++){
 86             int a,b;
 87             scanf("%d%d",&a,&b);
 88             flag[a][b] = true;
 89         }
 90         for(int i = 1;i <= n;i++){
 91             for(int j = 1;j <= m;j++){
 92                 if((i + j) % 2 == 0){
 93                     if(flag[i][j])  add_edge(S,ID(i,j),INF);
 94                     else            add_edge(S,ID(i,j),score[i][j]);
 95                     for(int k = 0;k < 4;k++){
 96                         int x = i + dx[k];
 97                         int y = j + dy[k];
 98                         if(ID(x,y) != 0){
 99                             //printf("i = %d,j = %d,k = %d,x = %d,y = %d\n",i,j,k,x,y);
100                             add_edge(ID(i,j),ID(x,y),2*(score[i][j]&score[x][y]));
101                         }
102                     }
103                 }else{
104                     if(flag[i][j])  add_edge(ID(i,j),T,INF);
105                     else            add_edge(ID(i,j),T,score[i][j]);
106                 }
107             }
108         }
109         int maxflow = dinic();
110         printf("%d\n",sum - maxflow);
111     }
112     return 0;
113 }
View Code

 

转载于:https://www.cnblogs.com/zhexipinnong/p/3246421.html

乐播投屏是一款简单好用、功能强大的专业投屏软件,支持手机投屏电视、手机投电脑、电脑投电视等多种投屏方式。 多端兼容与跨网投屏:支持手机、平板、电脑等多种设备之间的自由组合投屏,且无需连接 WiFi,通过跨屏技术打破网络限制,扫一扫即可投屏。 广泛的应用支持:支持 10000+APP 投屏,包括综合视频、网盘与浏览器、美韩剧、斗鱼、虎牙等直播平台,还能将央视、湖南卫视等各大卫视的直播内容一键投屏。 高清流畅投屏体验:腾讯独家智能音画调校技术,支持 4K 高清画质、240Hz 超高帧率,低延迟不卡顿,能为用户提供更高清、流畅的视觉享受。 会议办公功能强大:拥有全球唯一的 “超级投屏空间”,扫码即投,无需安装。支持多人共享投屏、远程协作批注,PPT、Excel、视频等文件都能流畅展示,还具备企业级安全加密,保障会议资料不泄露。 多人互动功能:支持多人投屏,邀请好友加入投屏互动,远程也可加入。同时具备一屏多显、语音互动功能,支持多人连麦,实时语音交流。 文件支持全面:支持 PPT、PDF、Word、Excel 等办公文件,以及视频、图片等多种类型文件的投屏,还支持网盘直投,无需下载和转格式。 特色功能丰富:投屏时可同步录制投屏画面,部分版本还支持通过触控屏或电视端外接鼠标反控电脑,以及在投屏过程中用画笔实时标注等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值