DP/记忆化搜索+set

题目:

HDU5218

 

 

题意:

给定一个环的大小N,给定一个大小为M的集合,包含的元素为可一次走的步数。初始环大小为N,全满并从1~N标号,每次从集合中随机选择一个元素,然后走a步,并移除当前走到的位置的前一个环上的点。求所有可能成为最后一个留下的元素原来的编号

 

 

方法:

DP,set

 

每次移除一个点后,就将环从当前节点开始重新从1开始标号(即每次走的时候都是从编号为0的点开始),然后反向倒推即可(为了方便计算,将标号从0开始记,即所有标号减小1)

 

假定集合中只有3个数a1,a2,a3。

 

第N-1步后:只留下了一个点,标记为0,即为最后一个留下的数

 

第N-2步后:留下2个点,标记为0,1。走过一步后可能留下的数为(a1%2),(a2%2),(a3%2),即在N-2步时编号为(a1%2),(a2%2),(a3%2)的点均有可能最后留下

 

第N-3步后:留下3个点,标记为0,1,2。走过一步后可能为N-2步编号为(a1%2),(a2%2),(a3%2)的点有:((a1+(a1%2))%3),((a2+(a1%2))%3),((a3+(a1%2))%3),这三个点在走过一步后均有可能成为N-2步时的(a1%2),即在第N-1步时可能被留下。同理,还有可能最后被留下的点有:((a1+(a2%2))%3),((a2+(a2%2))%3),((a3+(a2%2))%3),((a1+(a3%2))%3),((a2+(a3%2))%3),((a3+(a3%2))%3)。

……

 

以上的点由于会在取模后出现重复,且最后输出从小到大,所以可以选用set进行存储

 

 

 

 

 

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <map>
#include <vector>
#include <cmath>
#include <queue>
#include<set>
#include <cstddef>

using namespace std;
typedef long long ll;

const int maxn = 2e3+100;
const ll mod = 1e6+7;
const double inf = 0x3f3f3f3f;
const double esp = 1e-6;

int N, M, a[210];
set<int> f[210];

int main()
{
	//freopen("in.txt", "r", stdin);

	int T;
	scanf("%d", &T);
	while( T-- )
	{
		scanf("%d%d", &N, &M);
		for (int i = 0; i < M ; i++){
			scanf("%d", &a[i]);
		}

		f[0].clear();
		f[0].insert(0);
		for (int i = 1; i < N; i++){
			f[i].clear();
			for (set<int>::iterator it = f[i-1].begin(); it != f[i-1].end(); it++){
				for (int j = 0; j < M; j++){
					f[i].insert(((*it) + a[j]) % (i + 1));
				}
			}
		}

		cout<<f[N-1].size()<<endl;
		for(set<int>::iterator it = f[N-1].begin(); it != f[N-1].end(); ) {
			printf("%d", (*it) + 1);
			it++;
			printf("%c", (it == f[N-1].end())? '\n':' ');
		}
	}


	return 0;
}

点赞