ZOJ 3213 Beautiful Meadow 解题报告(插头DP)

Beautiful Meadow Time Limit: 5 Seconds       Memory Limit: 32768 KB ZOJ 3213 Beautiful Meadow 解题报告(插头DP)_第1张图片
Tom's Meadow

Tom has a meadow in his garden. He divides it into N * M squares. Initially all the squares are covered with grass and there may be some squares cannot be mowed.(Let's call them forbidden squares.) He wants to mow down the grass on some of the squares. He must obey all these rules:

1 He can start up at any square that can be mowed. 
2 He can end up at any square that can be mowed.
3 After mowing one square he can get into one of the adjacent squares. 
4 He cannot get into any of the forbidden squares.
5 He cannot get into the squares that he has already mowed.
6 If he is in some square he must mow it first. (and then decide whether to mow the adjacent squares or not.)
7 Each square that can be mowed has a property D called beauty degree (D is a positive integer) and if he mowed the square the beauty degree of the meadow would increase by D.
8 Note that the beauty degree of the meadow is 0 at first.
9 Of course he cannot move out of the meadow. (Before he decided to end.)
Two squares are adjacent if they share an edge.

Here comes the problem. What is the maximum beauty degree of the meadow Tom can get without breaking the rules above.

Input

This problem has several test cases. The first line of the input is a single integer T (1 <= T < 60) which is the number of test cases. T consecutive test cases follow. The first line of each test case is a single line containing 2 integers N (1 <= N < 8) and M (1 <= M < 8) which is the number of rows of the meadow and the number of columns of the meadow. Then N lines of input describing the rows of the meadow. Each of the N lines contains M space-separated integers D (0 <= D<= 60000) indicating the beauty degree of the correspoding square. For simplicity the beauty degree of forbidden squares is 0. (And of course Tom cannot get into them or mow them.)

Output

For each test case output an integer in a single line which is maximum beauty degree of the meadow at last.

Sample Input

2
1 1
10
1 2
5 0

Sample Output

10
5


    解题报告: 插头DP专题的最后一题了= =。

    分析易知,肯定有两个独立插头。加一维控制插头数量,最后输出插头数量为2,状态为0的值即可。

    有个特例,只取一个数,特殊处理下即可。

    代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn=29989;
const int L=2;
int now,pre;
int n,m;
int maze[10][10];
struct Node
{
	int H[maxn];
	int S[maxn];
	int N[maxn];
	int size;
	void init()
	{
		memset(H,-1,sizeof(H));
		size=0;
	}

	void push(int SS,int num)
	{
		int s=SS%maxn;
		while(~H[s] && S[H[s]]!=SS)
			s=(s+1)%maxn;
		if( ~H[s] )
		{
			N[H[s]]=max(N[H[s]],num);
		}
		else
		{
			S[size]=SS;
			N[size]=num;
			H[s]=size++;
		}
	}

	int get(int SS)
	{
		int s=SS%maxn;
		while(~H[s] && S[H[s]]!=SS)
			s=(s+1)%maxn;
		if( ~H[s] )
			return N[H[s]];
		return 0;
	}
} dp[2][3];

int get(int S,int p)
{
	return (S>>(p*L))&((1<<L)-1);
}

void set(int &S,int p,int v)
{
	S^=get(S,p)<<(p*L);
	S^=v<<(p*L);
}

void findOne(int &S,int j,int val)
{
	int find=1;
	for(int l=j-1;l>=0;l--)
	{
		int v=get(S,l);
		if(v==2) find++;
		if(v==1) find--;
		if(find==0)
		{
			set(S,l,val);
			return;
		}
	}
}

void findTwo(int &S,int j,int val)
{
	int find=1;
	for(int l=j+2;l<=m;l++)
	{
		int v=get(S,l);
		if(v==1) find++;
		if(v==2) find--;
		if(find==0)
		{
			set(S,l,val);
			return;
		}
	}
}

int main()
{
	int T;
	scanf("%d",&T);

	while(T--)
	{
		scanf("%d%d",&n,&m);
		memset(maze,0,sizeof(maze));
		for(int i=0;i<n;i++) for(int j=0;j<m;j++)
			scanf("%d",&maze[i][j]);

		now=1,pre=0;
		for(int c=0;c<=2;c++) dp[now][c].init();
		dp[now][0].push(0,0);

		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				int val=maze[i][j];

				swap(now,pre);
				for(int c=0;c<=2;c++) dp[now][c].init();

				for(int c=0;c<=2;c++) for(int s=0;s<dp[pre][c].size;s++)
				{
					int S=dp[pre][c].S[s];
					int num=dp[pre][c].N[s]+val;
					int p=get(S,j);
					int q=get(S,j+1);
					int ma=max(p,q);
					int mi=min(p,q);

					if(val==0)
					{
						if(p==0 && q==0)
							dp[now][c].push(S,num-val);
						continue;
					}

					if(ma==0)	// 0,0
					{
						dp[now][2].push(0,val);
						dp[now][c].push(S,num-val);
						
						set(S,j,1);
						set(S,j+1,2);
						if(maze[i][j+1] && maze[i+1][j])
							dp[now][c].push(S,num);

						if(c==2) continue;
						set(S,j,3);
						set(S,j+1,0);
						if(maze[i+1][j])
							dp[now][c+1].push(S,num);

						set(S,j,0);
						set(S,j+1,3);
						if(maze[i][j+1])
							dp[now][c+1].push(S,num);
					}
					else if(mi==0)	// 0,x x,0
					{
						if(maze[i+(p>0)][j+(q>0)])
							dp[now][c].push(S,num);

						swap(q,p);
						set(S,j,p);
						set(S,j+1,q);
						if(maze[i+(p>0)][j+(q>0)])
							dp[now][c].push(S,num);

						if(c==2) continue;
						set(S,j,0);
						set(S,j+1,0);
						if(ma==1) findTwo(S,j,3);
						if(ma==2) findOne(S,j,3);
						dp[now][c+1].push(S,num);
					}
					else
					{
						if(p==1 && q==2) continue;
						if(mi==1 && ma!=2) findTwo(S,j,ma);	// 1,1 1,3 3,1
						if(mi==2 && ma>=2) findOne(S,j,ma);	// 2,2 2,3 3,2
						set(S,j,0);
						set(S,j+1,0);
						dp[now][c].push(S,num);
					}
				}
			}
			for(int c=0;c<=2;c++) for(int s=0;s<dp[now][c].size;s++)
				dp[now][c].S[s]<<=L;
		}

		printf("%d\n",dp[now][2].get(0));
	}
}


    最小表示法的代码。最小表示法的状态转移就简单很多了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
const int maxn=29989;
const int L=3;
const int K=2;
int n,m;
int now,pre;
int maze[15][15];
int code[15],ch[15];
struct Node
{
	int h[maxn];
	LL s[maxn];
	int n[maxn];
	int size;
	void init()
	{
		size=0;
		memset(h,-1,sizeof(h));
	}
	void push(LL ss,int num)
	{
		int i=ss%maxn;
		while( ~h[i] && s[h[i]]!=ss )
			i=(i+1)%maxn;
		if( ~h[i] )
		{
			n[h[i]]=max(n[h[i]],num);
		}
		else
		{
			s[size]=ss;
			n[size]=num;
			h[i]=size++;
		}
	}
	int get(LL ss)
	{
		int i=ss%maxn;
		while( ~h[i] && s[h[i]]!=ss )
			i=(i+1)%maxn;
		if( ~h[i] )
			return n[h[i]];
		return 0;
	}
} dp[2][3];

void decode(LL s)
{
	for(int i=0;i<=m;i++)
		code[i]=s&((1<<L)-1),s>>=L;
}

LL encode()
{
	memset(ch,-1,sizeof(ch));
	ch[0]=0;
	int cnt=1;
	LL s=0;
	for(int i=m;i>=0;i--)
	{
		if(ch[code[i]]==-1)
			ch[code[i]]=cnt++;
		s<<=L;
		s|=ch[code[i]];
	}
	return s;
}

void shift()
{
	for(int k=0;k<=K;k++) for(int s=0;s<dp[now][k].size;s++)
		dp[now][k].s[s]<<=L;
}

void merge(int a,int b)
{
	for(int i=0;i<=m;i++) if(code[i]==a)
		code[i]=b;
}

void dpGrid(int i,int j)
{
	for(int k=0;k<=K;k++) for(int s=0;s<dp[pre][k].size;s++)
	{
		decode(dp[pre][k].s[s]);
		int num=dp[pre][k].n[s];
		int left=code[j];
		int up=code[j+1];
		int ma=max(left,up);
		int mi=min(left,up);
		int val=maze[i][j];
		num+=val;

		if(val==0)
		{
			if(ma==0)
				dp[now][k].push(encode(),num-val);
			continue;
		}

		if(ma==0)
		{
			dp[now][K].push(0,val);
			dp[now][k].push(encode(),num-val);
			if(maze[i][j+1] && maze[i+1][j])
			{
				code[j]=code[j+1]=13;
				dp[now][k].push(encode(),num);
			}

			if(k==K) continue;
			if(maze[i+1][j])
			{
				code[j]=13;
				code[j+1]=0;
				dp[now][k+1].push(encode(),num);
			}
			if(maze[i][j+1])
			{
				code[j]=0;
				code[j+1]=13;
				dp[now][k+1].push(encode(),num);
			}
		}
		else if(mi==0)
		{
			code[j]=ma;
			code[j+1]=0;
			if(maze[i+1][j])
				dp[now][k].push(encode(),num);

			code[j]=0;
			code[j+1]=ma;
			if(maze[i][j+1])
				dp[now][k].push(encode(),num);

			if(k==K) continue;
			code[j]=0;
			code[j+1]=0;
			dp[now][k+1].push(encode(),num);
		}
		else if(left!=up)
		{
			code[j]=code[j+1]=0;
			merge(left,up);
			dp[now][k].push(encode(),num);
		}
	}
}

void solve()
{
	now=1;
	pre=0;
	for(int k=0;k<=K;k++) dp[now][k].init();
	dp[now][0].push(0,0);

	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			swap(now,pre);
			for(int k=0;k<=K;k++) dp[now][k].init();
			dpGrid(i,j);
		}
		shift();
	}
}

void init()
{
	scanf("%d%d",&n,&m);

	memset(maze,0,sizeof(maze));
	for(int i=0;i<n;i++) for(int j=0;j<m;j++)
		scanf("%d",&maze[i][j]);
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		init();
		solve();
		printf("%d\n",dp[now][K].get(0));
	}
}


你可能感兴趣的:(dp,插头DP)