M - Integer Transmission UVA - 1228 详细题解

问题简述: 给你一个n比特的非负整数k,比如3比特的正整数2就表示010,然后我们传输这个数,每个比特从左到右传输,也就是一个比特一个比特的传,其中每个比特都可能会有0~d的延迟,若同时有多个比特同时被传送过来,实际接受到的顺序任意,求实际收到的整数有多少种,已及他们的最大值最小值

很有难度的一道题,不过这道题对我的启发意义非常大

具体做法参考自刘汝佳,我这里主要是将他的思路说的更详细更清楚些

首先做这样一个规定,所有的0按原来的顺序依次收到,所有的1也按原来的顺序依次收到

这样做的好处先不说,因为没法说,这里说一下这样规定为什么是正确的

考虑样例 1110010  d=5 ,假设最终形成了某一个接收序列是1100110,或者是1001101,你能看出哪个一先到哪个一后到?换句话说,如果形成的序列是这个样子的,我们根本没法去知道这些1的谁先来谁后来,而且不管谁先来谁后来,只要他们占的位置一样,最终的结果是一样的,而且就算实际是后面的先到,我们也可以认为是前面的先到,因为前面肯定能到这个位置,而后面的肯定也能到前面能到的位置,所以这样的规定是正确的

最大值最小值用贪心很容易求出来,这里就不说了

给一个整数P,我们怎么去判断它是否能收到?

根据我们前面的规定,我们选择用贪心来解决这个问题,假设P的第一个比特位是0,我们肯定让原序列中第一个0先到,如果第一个0到不了,自然不能收到,否则,接着判断,如果是1也是一样的道理,如果推到最后一位都能被收到,那么说明P可以收到,然后我们发现已经接受到的序列,最后一个序列一定是没有延迟的,或者是可以延迟,但结果不会更好,因为本来就是让它先到最好,自然越快越好

到这里都比较好理解,关键是接下的递推方程,刘汝佳并没有讲述这个方程是怎么求出来的,想了好久这个应该怎么去做,还是没有头绪,然后恰好看到书上一处被我忽略的地方,就是对这样一类DP问题的简述,原话是这样的:准确的说这不是动态规划,而是组合数学的递推,因为本题不是最优化问题,而是计数问题,不过解决两个问题的思路是一样的,所以很多人也把组合数学中的递推叫做动态规划。

就这样一句话,似乎对解题没有什么帮助,但让我回忆起来似乎好像曾经思考这样的问题,也是计数问题,用的思想也是动态规划,我当时再想的就是为什么这样的题也是动态规划,动态规划不是求最优的?这里是求数量,但是它的确被放在了动态规划的目录下,怎么办?然后我发现其中很重要的一环,决策,没错,为什么是动态规划,就是因为决策,或许是为了求数量,但思想是一样的,定义状态考虑决策,更新状态,然后我就去重新审视递推方程,他的决策是什么?

虽然没有一下看出来,但是很怪,我感觉我好像已经会了

我尝试去跳出这道题,我思考了这样一个问题,

如果一个长度为n的序列,序列上的每一个位置可以是0或者1,结果有多少种

如果是以前我毫不犹豫的说2^n,但我重新去用动态规划的方法去思考它

我先定义状态,用d(k)表示长度为k的序列的可能的情况

然后我马上就得到了这样一个递推方程,d(k)=d(k-1)*2

但这不够,我再去考虑我是怎么得到这个递推式的,d(k-1)知道说明我们已经放了n-1个数了,我们在去考虑放第k个数

然后有两种情况放0或者放1,所以我乘以2

再去想,这不就是决策?决策1是放1,决策二是放0,然后加起来

也就 if(第k个数字是0) d(k)+=d(k-1)

if(第k个数字是1) d(k)+=d(k-1)

显然这是正确的,而且这好像和题目上的递推方程及其相似

然后再去进一步思考,依旧是用动态规划思想

我们去考虑状态和决策

再联系题目,如果我们要拿5个1和5个0组成一个序列,有多少种情况

显然不能再去用以前的状态定义了,因为我们不知道前面用了多少0,多少1,而这影响我们的决策

所以我们增加一维,把0的个数和1的个数加进状态

用d(i)(j)表示有用i个0和j个1组成的序列的情况总数

然后我们去考虑递推方程

d(i)(j)+=d(i-1)(j) if(i<5)

d(i)(j)+=d(i)(j-1) if(j<5)

写完之后我们发现,这基本上和紫书上的方程一样了只不过条件判断不一样

有了这样的理解我们去考虑紫书上的递推方程怎么来的,似乎不用去想了,这两个问题几乎是一样的

只要能放0或者1我们就去放

再仔细思考紫书前面说的一个结论,已经接收到的中最后一个一定是没延迟的

这不就是告诉我们能在构造的时候能放就放?

有种做了一道题好像做了10道题的感觉,计数问题用动态规划,希望有这样的理解后可以做出来

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<assert.h>
#include<vector>
#include<list>
#include<map>
#include<set>
#include<sstream>
#include<stack>
#include<queue>
#include<string>
#include<bitset>
#include<algorithm>
#pragma warning(disable:4996)
#define me(s)  memset(s,0,sizeof(s))
#define _for(i,a,b) for(int i=(a);i<(b);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef pair <int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
const int MOD = 1e9 + 7;
const double pi = acos(-1.0);
const int maxn = 64;
int n, d, K[maxn];
ull f[maxn + 1][maxn + 1];
int zcnt, ocnt;
int Z[maxn], O[maxn];
ull minv, maxv;
bool can_receive_zero(int i, int j)
{
	return i + 1 <= zcnt && (j == ocnt || O[j] + d >= Z[i]);
}

bool can_receive_one(int i, int j)
{
	return j + 1 <= ocnt && (i == zcnt || Z[i] + d >= O[j]);
}

void greedy()
{
	minv = maxv = 0;
	int i = 0, j = 0;
	while (i < zcnt || j < ocnt) {
		if (can_receive_zero(i, j)) { i++; minv = minv * 2; }
		else { j++; minv = minv * 2 + 1; }
	}
	i = j = 0;
	while (i < zcnt || j < ocnt) {
		if (can_receive_one(i, j)) { j++; maxv = maxv * 2 + 1; }
		else { i++; maxv = maxv * 2; }
	}

}
void solve()
{
	ocnt = zcnt = 0;
	_for(i, 0, n) if (K[i] == 1) O[ocnt++] = i;
	else Z[zcnt++] = i;
	greedy();
	me(f);
	f[0][0] = 1;
	for (int i = 0; i <= zcnt; i++)
		for (int j = 0; j <= ocnt; j++) {
			if (can_receive_zero(i, j)) f[i + 1][j] += f[i][j];
			if (can_receive_one(i, j)) f[i][j + 1] += f[i][j];
		}
	cout << f[zcnt][ocnt] << " " << minv << " " << maxv << "\n";
}
int main()
{
	int kcase = 0;
	ull k;
	while (cin >> n >> d >> k) {
		for (int i = 0; i < n; i++) {
			K[n - i - 1] = k % 2; k /= 2;
		}
		cout << "Case " << ++kcase << ": ";
		solve();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41776911/article/details/82891889