比赛时看完了 4 5 6 题,第四题感觉是线段树,想等会儿让HG 写,5 题没看懂,是后来LM给我讲的,所以一开始一直在研究 6,等LM 跟HG的题都过了,6 的样例还是没出来,然后跟LM一起想,还是没思路,决定去看别的题了,最后HG说可能是置换群的题目,他说他正好刚刷过去,说让他想吧,比完赛一看确实是有关置换的,但我们还是没做出来,5 后来到是猜到了一点的意思,但是看了题解后我才知道自己是多么的无知,竟然想着用打表的方式过,比赛还是充分体现了自己没学到的很多,学到了不会用的也很多,多做多练思路是很有必有的
下面都是比赛时过题较多的,做过的无视就可以了
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4341
题意:给出一些点坐标,表示这个点上有物体价值为val,需要的时间为 tim,问在给定的时间内可以获得的最大价值,有一个限制,如果在一条线上的获取宝贝必须按顺序来
思路:把给的点先进行排序,然后统计出斜率相同的,然后再重新处理价值和时间,在进行二维背包
1 #include <iostream> 2 #include <string.h> 3 #include <algorithm> 4 #include <stack> 5 #include <queue> 6 #include <math.h> 7 #include <stdlib.h> 8 #include <stdio.h> 9 #define N 210 10 #define inf 100000000 11 #define _clr(a,val) (memset(a,val,sizeof(a))) 12 13 using namespace std; 14 15 struct node 16 { 17 int x; 18 int y; 19 int val; 20 int tim; 21 int dis; 22 }point[N]; 23 int val[N][N],tim[N][N]; 24 bool vis[N]; 25 int back[N][N]; 26 int dp[N * N]; 27 bool cmp(node a,node b) 28 { 29 return a.dis < b.dis; 30 } 31 bool dist(node a,node b) 32 { 33 if(a.x * b.y == a.y * b.x) return true; 34 else return false; 35 } 36 int main() 37 { 38 int i,j,k; 39 int n,t; 40 int cs = 0; 41 //freopen("data.txt","r",stdin); 42 while(scanf("%d%d",&n,&t) != EOF) 43 { 44 for(i = 0; i < n; i++) 45 { 46 scanf("%d%d%d%d",&point[i].x,&point[i].y,&point[i].tim,&point[i].val); 47 point[i].dis = point[i].x * point[i].x + point[i].y * point[i].y; // 排序时的参数 48 } 49 sort(point,point + n,cmp); 50 _clr(vis,0); 51 _clr(back,0); 52 int num = 0; 53 for(i = 0; i < n; i++) 54 { 55 if(!vis[i]) 56 { 57 num++; 58 for(j = i; j < n; j++) 59 { 60 if(!vis[j]) 61 { 62 if(dist(point[i],point[j]) == true) // 统计斜率相同的点 63 { 64 vis[j] = true; 65 back[num][0]++; // 保存个数 66 back[num][back[num][0]] = j; // 保存第几个点 67 } 68 } 69 } 70 } 71 } 72 for(i = 1; i <= num; i++) 73 { 74 val[i][0] = 0; 75 tim[i][0] = 0; 76 for(j = 1; j <= back[i][0]; j++) 77 { 78 val[i][j] = val[i][j - 1] + point[back[i][j]].val; // 重新计算价值和时间 79 tim[i][j] = tim[i][j - 1] + point[back[i][j]].tim; 80 } 81 } 82 _clr(dp,0); 83 for(i = 1; i <= num; i++) 84 { 85 for(j = t; j >= 0; j--) 86 { 87 for(k = 0; k <= back[i][0]; k++) 88 { 89 if(j >= tim[i][k]) 90 { 91 dp[j] = max(dp[j],(dp[j - tim[i][k]] + val[i][k])); // 背包 92 } 93 } 94 } 95 } 96 printf("Case %d: %d\n",++cs,dp[t]); 97 } 98 return 0; 99 }
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4342
题意:给一个n 让你求 第n个非平方数,并求出 从 1 到 n 的
这个值,也就是 开方不大于 i 的正整数
1 #include <stdio.h> 2 #include <string.h> 3 #include <math.h> 4 #include <iostream> 5 6 using namespace std; 7 8 typedef long long ll; 9 ll capow(ll x) 10 { 11 return x * x; 12 } 13 int main() 14 { 15 ll i,j; 16 int t; 17 ll n; 18 //int n; 19 //freopen("data.txt","r",stdin); 20 scanf("%d",&t); 21 while(t--) 22 { 23 cin>>n; 24 i = 1; 25 ll sum = 0; 26 while(1) 27 { 28 if(n < capow(i + 1) - capow(i) - 1) 29 break; 30 else 31 { 32 sum += (capow(i + 1) - capow(i)) * i; // 根据给的公式,i 到 i + 1之间的数,除了 (i + 1) 开方 等于 (i + 1) 其他的都等于 i 33 n -= capow(i + 1) - capow(i) - 1; // (i + 1) 平方 到 i 平方之间的数全部为非平方数,这样不断的去降 n 34 i++; 35 } 36 } 37 int temp = i; 38 if(n == 0) 39 { 40 i = capow(i) - 1; 41 cout<<i<<" "<<sum<<endl; 42 continue; 43 } 44 else // 处理n 没有恰好降到零 45 { 46 i = capow(i); 47 while(n--) 48 { 49 i++; 50 } 51 } 52 ll tem = i; 53 if(capow(temp + 1) > i) // 处理剩余的没有加的数 54 { 55 tem = tem - capow(temp) + 1; 56 sum += temp * tem; 57 } 58 cout<<i<<" "<<sum<<endl; 59 } 60 return 0; 61 }
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4345
这个本来就是看标程才懂的,所以还是贴别人的讲解吧 循环节的长度为各独立置换环长度的最小公倍数。问题即求相加和为N的正整数的最小公倍数的可能数。由于1不影响最小公倍数,问题转化为相加小于等于N的若干正整数的最小公倍数的可能数。 如果这些正整数包含大于一个质因子,只会使得正整数的和更大。 因而问题再次转化为相加小于等于N的若干质数的最小公倍数的可能数。 N<1000,于是可递推得,标程用记忆化搜索实现的。
1 #include <cstdio> 2 #include <cstring> 3 #include <string.h> 4 #include <iostream> 5 #define _clr(a,val) (memset(a,val,sizeof(a))) 6 7 using namespace std; 8 9 typedef long long ll; 10 ll dp[210][1200]; 11 ll prim[210], num; 12 int vis[1010]; 13 void is_prim(int n) 14 { 15 int i,j,k; 16 for(i = 2; i <= n; i++) 17 { 18 if(vis[i] == 0) 19 { 20 prim[num ++] = i; 21 } 22 for(j = 0, k = i * prim[j]; j <= num && k <= n; j++,k = i * prim[j]) 23 { 24 vis[k] = 1; 25 if(i % prim[j] == 0) break; 26 } 27 } 28 } 29 ll cal(int x,int n) 30 { 31 if(x == num) return 1; 32 if(dp[x][n] != -1) return dp[x][n]; 33 dp[x][n] = cal(x + 1,n); 34 ll ans = prim[x]; 35 while(ans <= n) 36 { 37 dp[x][n] += cal(x + 1,n - ans); 38 ans *= prim[x]; 39 } 40 return dp[x][n]; 41 } 42 int main() 43 { 44 int n; 45 while(scanf("%d",&n) != EOF) 46 { 47 _clr(vis,0); 48 num = 0; 49 is_prim(n); 50 _clr(dp,-1); 51 cout<<cal(0,n)<<endl; 52 } 53 return 0; 54 }
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4349
思路:题解上是这样解释的本题为Lucas定理推导题,我们分析一下 C(n,m)%2,那么由lucas定理,我们可以写成二进制的形式观察,比如 n=1001101,m是从000000到1001101的枚举,我们知道在该定理中 C(0,1)=0,因此如果n=1001101的0对应位置的m二进制位为1那么C(n,m) % 2==0,因此m对应n为0的 位置只能填0,而1的位置填0,
1都是1(C(1,0)=C(1,1)=1),不影响结果为奇数,并且保证不会出n的范围,因此所有的情况即是n中1位置对应m位置0,1的枚举,那么结果很明显就是:2^(n中1的个数)
1 #include <stdio.h> 2 #include <string.h> 3 #include <math.h> 4 #include <iostream> 5 6 using namespace std; 7 8 typedef long long ll; 9 int main() 10 { 11 int i,j; 12 ll sum; 13 int n; 14 while(scanf("%d",&n) != EOF) 15 //while(cin>>n) 16 { 17 int num = 0; 18 while(n) 19 { 20 if(n % 2) num ++; 21 n /= 2; 22 } 23 sum = pow(2,num); 24 printf("%I64d\n",sum); 25 //cout<<sum<<endl; 26 } 27 return 0; 28 }