题目:
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;
}