cf1209E.Rotate Columns(div2)

拦住我的好题(状压dp)

题意:

  • 给定一个矩阵 n × m n\times m n×m, 现可以任意旋转任意一列, 求该矩阵, 在操作范围内对每行的最大值求和

easy-version数据范围: n ≤ 4 , m ≤ 100 n \leq 4,m \leq100 n4,m100

>> face <<

前置技能:状压dp

Tutorial: 看到数据范围, 可以存下所有的状态, 而且n很小, 设计状态

状态 d p [ i ] [ j ] : 前 i 列 , 选 取 最 大 值 状 态 为 j 的 最 大 值 dp[i][j]:前i列,选取最大值状态为j的最大值 dp[i][j]:i,j

#include <bits/stdc++.h>
using namespace std;
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define oo 0x3f3f3f3f
#define ll long long
#define db double
#define eps 1e-8
#define bin(x) cout << bitset<10>(x) << endl;
#define what_is(x) cerr << #x << " is " << x << endl
#define met(a, b) memset(a, b, sizeof(a))
#define all(x) x.begin(), x.end()
#define pii pair<int, int>
int nxt()
{
	int ret = 0;
	scanf("%d", &ret);
	return ret;
}
signed main()
{
	int t = nxt();
	while (t--)
	{
		int n = nxt(), m = nxt();
		vector<vector<int>> dp(m + 1, vector<int>(1 << n, 0)), mat(n + 1, vector<int>(m + 1, 0));
		_rep(i, 1, n) _rep(j, 1, m) mat[i][j] = nxt();
		_rep(i, 1, m)
		{
			_for(cur, 0, 1 << n)
			{
				for (int sub = cur;; sub = (sub - 1) & cur)
				{
					int res = 0, add = cur - sub;
					_rep(r, 0, n - 1)
					{
						int val = 0;
						_rep(j, 0, n - 1)
						{
							if (add & (1 << j))
								val += mat[(r + j) % n + 1][i];
						}
						res = max(res, val);
					}
					dp[i][cur] = max(dp[i][cur], dp[i-1][sub] + res);
					if(sub == 0)break;
				}
			}
		}
		cout << dp[m][(1 << n) - 1] << endl;
	}
}

hard-version数据范围: n ≤ 12 , m ≤ 2000 n \leq 12,m \leq 2000 n12,m2000

>> face <<

Tutorial: 维护每一列的最大值, 显然, 我们只会用到最大值最大的前n列(每个值对应一行), 所以肯定只用在前n列中跑一遍状压, 另外n从4变到12, 子状态那里有重复计算的痕迹, 考虑预处理出第i大列选择状态为j的最大值pre[i][j];

#include <bits/stdc++.h>

using namespace std;
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define oo 0x3f3f3f3f
#define ll long long
#define db double
#define eps 1e-8
#define bin(x) cout << bitset<10>(x) << endl;
#define what_is(x) cerr << #x << " is " << x << endl
#define met(a, b) memset(a, b, sizeof(a))
#define all(x) x.begin(), x.end()
#define pii pair<int, int>
const int maxn = 2e3 + 5;
int nxt()
{
	int ret = 0;
	scanf("%d", &ret);
	return ret;
}
signed main()
{
	int t = nxt();

	while (t--)
	{
		int n = nxt(), m = nxt(), cnt = 0;
		vector<vector<int>> dp(2, vector<int>(1 << n, 0)), mat(n + 1, vector<int>(m + 1, 0));
		vector<pii> arr(m + 1);
		_rep(i, 0, m) arr[i].second = i;
		_rep(i, 1, n)
		{
			_rep(j, 1, m)
			{
				mat[i][j] = nxt();
				arr[j].first = max(arr[j].first, mat[i][j]);
			}
		}
		sort(arr.begin() + 1, arr.end(), [](pii a, pii b) { return a.first > b.first; });
		int q = min(m, n);
		vector<vector<int>> pre(q + 1, vector<int>(1 << n, 0));
		_rep(i, 1, q)
		{
			int id = arr[i].second;
			_rep(cur, 0, (1 << n) - 1) //所有状态
			{
				int res = 0;
				_for(s, 0, n)
				{ //起点
					int val = 0;
					_for(j, 0, n)
					{
						if (cur & (1 << j))
						{
							val += mat[(s + j) % n + 1][id];
						}
					}
					res = max(res, val);
				}
				pre[i][cur] = max(pre[i][cur], res);
			}
		}
		_rep(i, 1, min(m, n))
		{
			_for(cur, 0, 1 << n)
			{
				for (int sub = cur;; sub = (sub - 1) & cur)
				{
					int add = cur - sub;
					dp[cnt][cur] = max(dp[cnt][cur], dp[cnt ^ 1][sub] + pre[i][add]);
					if (sub == 0)
						break;
				}
			}
			cnt ^= 1;
		}
		cout << dp[cnt ^ 1][(1 << n) - 1] << endl;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值