方格取数(1)
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5589 Accepted Submission(s): 2123
Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3 75 15 21 75 15 28 34 70 5
Sample Output
188
思路:枚举每个状态,得到最优解。枚举上下两个状态时,应判断它们之间是否矛盾,用一个二进制数可以记录每一行的一种取法,即相邻的两位的数字不能都为1,间隔取数。
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define N 21 #define M 30000 int state[M]; int a[N][N]; int dp[N][M]; int n,lim; void inti() //得到一行能取的数的所有集合 { //每个数组元素代表一种取法 int i,k; for(i=k=0;i<=(1<<20);i++) { if(i&(i<<1)) //每个数字左移一位,若与自身相与为真 continue; //这个数字肯定有相邻两位都为1. state[k++]=i; //i=1101 ,左移一位后为ii=11010 , } } int getsum(int i,int x) //得到该种取法的数组和 { int t,sum; t=sum=0; while(x) { if(x&1) //相与为真则这个数字能取 sum+=a[i][t]; x>>=1; t++; } return sum; } void solve() { int i,j,k; lim=1<<n; for(i=0;state[i]<lim;i++) { dp[0][i]=getsum(0,state[i]); } for(i=1;i<n;i++) //遍历剩下的数组 { for(j=0;state[j]<lim;j++) //对于每一行可取的状态 { int tmp=0; for(k=0;state[k]<lim;k++) //找到上一行能取得最大值 { if(!(state[j]&state[k])) //相邻两行不矛盾 tmp=max(tmp,dp[i-1][k]); } dp[i][j]=tmp+getsum(i,state[j]); } } int ans=0; for(i=0;state[i]<lim;i++) ans=max(ans,dp[n-1][i]); printf("%d\n",ans); } int main() { int i,j; inti(); while(scanf("%d",&n)!=-1) { if(n==0) //特判 { printf("0\n"); continue; } for(i=0;i<n;i++) { for(j=0;j<n;j++) { scanf("%d",&a[i][j]); } } solve(); } return 0; }
时间: 2024-10-15 15:13:40