题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5943
题意:给你两个数n, s 然后让你判断是否存在(s+1, s+2, s+3, ... , s+n )的任意排列方式使得每个数都满足当前数num,与num所在位置 pos 形成num%pos=0;
例如 n = 4 , s = 11
num = {13, 14, 15, 12}
pos = {1, 2, 3, 4}
每个num与之对应的pos都是num%pos = 0;的关系
分为两种情况:当s+1>n 时,我们只需看第二个数组中是否含有>=2个素数即可, 因为1只有1个;
当s+1<=n时,两个区间有重叠,我们可以忽略这些重叠部分,看两端的即可,其实就是交换一下n,s的值;
其实就是两个数组建图,然后求最大匹配是否为1,当然n比较大,但是连续600个数中间一定包含>=2个素数,所以当n<600时可以用二分匹配来做;
#include<stdio.h> #include<string.h> #include<iostream> #include<vector> using namespace std; typedef long long LL; const int oo = 0xfffffff; const int N = 615; int used[N], vis[N], n, s; vector<int> G[N]; bool Find(int u) { int len=G[u].size(); for(int i=0; i<len; i++) { int v = G[u][i]; if(!vis[v]) { vis[v] = 1; if(!used[v] || Find(used[v])) { used[v] = u; return true; } } } return false; } int main() { int T, t = 1; scanf("%d", &T); while(T --) { scanf("%d %d", &n, &s); if(s+1 <= n)///忽略中间重合的部分; swap(s, n); if(n > 600)///因为连续600个数中一定有>=2个素数, 1只有一个,所以结果都是No; { printf("Case #%d: No ", t++); continue; } for(int i=0; i<=n; i++) G[i].clear(); for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { if((s+i)%j == 0)///说明s+i是j的倍数; G[i].push_back(j); } } memset(used, 0, sizeof(used)); int ans = 0; for(int i=1; i<=n; i++) { memset(vis, 0, sizeof(vis)); ans += Find(i); } if(ans != n)///必须要找到所有与之对应的才可以; printf("Case #%d: No ", t++); else printf("Case #%d: Yes ", t++); } return 0; }