P2774 方格取数问题
题目背景
none!
题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入输出格式
输入格式:
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。
输出格式:
程序运行结束时,将取数的最大总和输出
输入输出样例
说明
m,n<=100
//状压DP //。貌似状压不能过,因为n,m都是<=100的, //这样找出来就是100个二进制位,也就是2^100 //.但是不知道为啥他们都能AC //写写试试 //91分封顶。 //数据还是太水。 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int N=1e2+5; const int M=1e5+5; inline int read() { char c=getchar();int num=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num; } int n,m; int map[N][N]; int sum[N][M]; int opt[N],cnt; int dp[N][M]; void init(int line,int id) { for(int i=1;i<=m;++i) if(opt[id]>>(i-1)&1) sum[line][id]+=map[line][i]; } int main() { n=read(),m=read(); for(int i=0;i<(1<<m);++i) { if(!(i&(i<<1)||i&(i>>1))) //找出不会有两个1相邻的状态 opt[++cnt]=i; if(i>2&&(i>>(i-1))&((i-1)>>(i-2))) i+=1<<i-2,--i; } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) map[i][j]=read(); for(int i=1;i<=cnt;++i) init(1,i),dp[1][i]=sum[1][i]; for(int i=2;i<=n;++i) for(int j=1;j<=cnt;++j) init(i,j); for(int i=2;i<=n;++i) //枚举第几行 { for(int j=1;j<=cnt;++j) //枚举第i行的状态 { for(int k=1;k<=cnt;++k) //枚举第i-1行的状态 { if(opt[j]&opt[k]) continue; dp[i][j]=max(dp[i][j],dp[i-1][k]+sum[i][j]); } } } int ans=0; for(int i=1;i<=cnt;++i) { // printf("%d %d\n",i,dp[n][i]); ans=max(ans,dp[n][i]); } printf("%d",ans); return 0; }
//网络流 //方格取数 //思考一下这个问题的性质 //就是个棋盘的黑白染色问题 //假设(i+j)&1==1的格子是黑格 //那么我们可以建一个二分图 // #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=1e5+5; const int INF=0x3f3f3f3f; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=f=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } int n,m,S,T; int head[N],from[N],num_edge; struct Edge { int v,flow,nxt; }edge[N]; void add_edge(int u,int v,int flow) { edge[++num_edge].v=v; edge[num_edge].flow=flow; edge[num_edge].nxt=head[u]; head[u]=num_edge; } int dep[N]; bool bfs() { queue<int> que; int now,v; for(int i=S;i<=T;++i) dep[i]=0,from[i]=head[i]; dep[S]=1; que.push(S); while(!que.empty()) { now=que.front(),que.pop(); for(int i=head[now];i;i=edge[i].nxt) { v=edge[i].v; if(!dep[v]&&edge[i].flow) { dep[v]=dep[now]+1; if(v==T) return 1; que.push(v); } } } return 0; } int dfs(int now,int flow) { if(now==T||!flow) return flow; int outflow=0,tmp; for(int i=head[now],v;i;i=edge[i].nxt) { if(!edge[i].flow) continue; v=edge[i].v; if(dep[v]!=dep[now]+1) continue; tmp=dfs(v,min(flow,edge[i].flow)); if(!tmp) continue; edge[i].flow-=tmp; edge[i^1].flow+=tmp; flow-=tmp; outflow+=tmp; if(!flow) break; } dep[now]=0; return outflow; } #define A m*(i-1)+j int ans; int main() { num_edge=1; n=read(),m=read(); T=n*m+1; for(int i=1,a;i<=n;++i) { for(int j=1;j<=m;++j) { a=read(); ans+=a; if((i+j)%2==0) { add_edge(A,T,a); add_edge(T,A,0); continue; } add_edge(S,A,a); add_edge(A,S,0); if(i>1) { add_edge(A,A-m,INF); add_edge(A-m,A,0); } if(j>1) { add_edge(A,A-1,INF); add_edge(A-1,A,0); } if(i<n) { add_edge(A,A+m,INF); add_edge(A+m,A,0); } if(j<m) { add_edge(A,A+1,INF); add_edge(A+1,A,0); } } } while(bfs()) { ans-=dfs(S,INF); } printf("%d",ans); return 0; }