• 【bzoj1187】[HNOI2007]神奇游乐园 插头dp


    题目描述

    给你一个 m * n 的矩阵,每个矩阵内有个权值V(i,j) (可能为负数),要求找一条回路,使得每个点最多经过一次,并且经过的点权值之和最大。

    输入

    输入文件中的第一行为两个正整数n和m,表示游乐场的大小为n×m。因为这个娱乐场很狭窄,所以n和m满足:2<=n<=100,2<=m<=6。接下来的n行,每行有m个整数,第i行第j列表示游乐场的第i行第j列的小格子中的娱乐项目的满意度,这个满意度的范围是[-1000,1000]。同一行的两个整数之间用空格隔开。

    输出

    输出文件中仅一行为一个整数,表示最高的满意度之和。

    样例输入

    4 4
    100 300 -400 400
    -100 1000 1000 1000
    -100 -100 -100 -100
    -100 -100 -100 1000

    样例输出

    4000


    题解

    插头dp

    设 $f[i][j][k]$ 表示轮廓线为第 $i$ 行第 $j$ 列,插头状态为 $k$ 的最大满意度。那么就和 哈密顿回路计数 那题按照同样的方法转移即可。

    注意本题不要求走遍所有格子,因此更新答案的时机应该为:左插头为左括号,上插头为右括号,其余位置没有插头。此时更新答案。注意此时不要转移,因为只能选择一条回路,不能选择多条。

    时间复杂度 $O(能过)$ 

    #include <cstdio>
    #include <cstring>
    int m , f[110][7][130] , a[110][7] , b[7] , w[2200] , v[130] , tot;
    inline void gmax(int &a , int b)
    {
    	a = (a > b ? a : b);
    }
    void dfs(int p , int c , int now)
    {
    	if(c < 0 || c > m - p + 1) return;
    	if(p > m)
    	{
    		w[now] = ++tot , v[tot] = now;
    		return;
    	}
    	dfs(p + 1 , c , now);
    	dfs(p + 1 , c + 1 , now + b[p]);
    	dfs(p + 1 , c - 1 , now + 2 * b[p]);
    }
    inline int right(int v , int p)
    {
    	int i , c = 0;
    	for(i = p ; i <= m ; i ++ )
    	{
    		if(v / b[i] % 3 == 1) c ++ ;
    		if(v / b[i] % 3 == 2) c -- ;
    		if(!c) return i;
    	}
    	return -1;
    }
    inline int left(int v , int p)
    {
    	int i , c = 0;
    	for(i = p ; ~i ; i -- )
    	{
    		if(v / b[i] % 3 == 2) c ++ ;
    		if(v / b[i] % 3 == 1) c -- ;
    		if(!c) return i;
    	}
    	return -1;
    }
    int main()
    {
    	int n , i , j , k , p , q , ans = -1 << 30;
    	scanf("%d%d" , &n , &m);
    	for(i = 1 ; i <= n ; i ++ )
    		for(j = 1 ; j <= m ; j ++ )
    			scanf("%d" , &a[i][j]);
    	b[0] = 1;
    	for(i = 1 ; i <= m ; i ++ ) b[i] = b[i - 1] * 3;
    	dfs(0 , 0 , 0);
    	memset(f , 0xc0 , sizeof(f));
    	f[1][0][w[0]] = 0;
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		for(j = 1 ; j <= m ; j ++ )
    		{
    			for(k = 1 ; k <= tot ; k ++ )
    			{
    				p = v[k] / b[j - 1] % 3 , q = v[k] / b[j] % 3;
    				if(!p && !q) gmax(f[i][j][k] , f[i][j - 1][k]);
    				if(!p && !q && i < n && j < m) gmax(f[i][j][w[v[k] + b[j - 1] + 2 * b[j]]] , f[i][j - 1][k] + a[i][j]);
    				if(!p && q)
    				{
    					if(j < m) gmax(f[i][j][k] , f[i][j - 1][k] + a[i][j]);
    					if(i < n) gmax(f[i][j][w[v[k] + q * (b[j - 1] - b[j])]] , f[i][j - 1][k] + a[i][j]);
    				}
    				if(p && !q)
    				{
    					if(i < n) gmax(f[i][j][k] , f[i][j - 1][k] + a[i][j]);
    					if(j < m) gmax(f[i][j][w[v[k] + p * (b[j] - b[j - 1])]] , f[i][j - 1][k] + a[i][j]);
    				}
    				if(p == 1 && q == 1) gmax(f[i][j][w[v[k] - b[j - 1] - b[j] - b[right(v[k] , j)]]] , f[i][j - 1][k] + a[i][j]);
    				if(p == 2 && q == 2) gmax(f[i][j][w[v[k] - 2 * b[j - 1] - 2 * b[j] + b[left(v[k] , j - 1)]]] , f[i][j - 1][k] + a[i][j]);
    				if(p == 2 && q == 1) gmax(f[i][j][w[v[k] - 2 * b[j - 1] - b[j]]] , f[i][j - 1][k] + a[i][j]);
    				if(p == 1 && q == 2 && !(v[k] - b[j - 1] - 2 * b[j])) gmax(ans , f[i][j - 1][k] + a[i][j]);
    			}
    		}
    		for(j = 1 ; j <= tot ; j ++ )
    			if(v[j] % 3 == 0)
    				f[i + 1][0][j] = f[i][m][w[v[j] / 3]];
    	}
    	printf("%d
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    StatusStrip控件的使用(转:http://blog.sina.com.cn/s/blog_4f18c3ec0100fguf.html)
    linux根文件系统
    git使用技巧
    修改git用户名
    luci中添加application
    openwrt安装依赖库
    STM32(二十九)定时器介绍
    openwrt部分文件解析
    uci.js文件解析
    矿机算力
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8127381.html
Copyright © 2020-2023  润新知