USACO 3.1 分析

题目一: Agri-Net

算法:MST

方法1:kruskal

要用到并查集的方法

int getfa(int x) {return fa[x] == x ? x : fa[x] = getfa(fa[x]); }

1.把边从小到大排序;

2.初始化:每个点自成一棵树,形成森林;

3.按边权值从小到大枚举,找到不在一棵树中的两个点,将这两棵树合并,累计权值;

小细节:记录下需要计算的代表元的结果;

#include <stdio.h> #include <string.h> const int max = 101*100+1; int u[max], v[max], w[max], fa[101], r[max]; void swap(int &a, int &b) { int temp = a; a = b; b = temp; } int getfa(int x) {return fa[x] == x ? x : fa[x] = getfa(fa[x]); } int main(){ freopen("agrinet.in", "r", stdin); freopen("agrinet.out", "w", stdout); int n; scanf("%d", &n); int tot = 0, d; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { scanf("%d", &d); if (i < j) { tot++; v[tot] = i; u[tot] = j; w[tot] = d; } } for (int i = 1; i < tot; i++) for (int j = i+1; j <= tot; j++) if (w[i] > w[j]) { swap(v[i], v[j]); swap(w[i], w[j]); swap(u[i], u[j]); } //for (int i = 1; i <= tot; i++) printf("%d %d %d/n", u[i], v[i], w[i]); for (int i = 0; i < n; i++) fa[i] = i; int cost = 0, rest = n-1; for (int i = 1; i <= tot && rest > 0; i++) { int x = getfa(u[i]), y = getfa(v[i]); if (x != y) { cost += w[i]; fa[x] = y; rest--; } } printf("%d/n", cost); return 0; }

 

方法二:PRIM

#include <string.h> #include <assert.h> const int max = 101; int map[max][max], vis[max], d[max]; int main(){ freopen("agrinet.in", "r", stdin); freopen("agrinet.out", "w", stdout); int n; scanf("%d", &n); for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) scanf("%d", &map[i][j]); /*intialize*/ int cost = 0; for (int i = 0; i < n; i++) { d[i] = (1 << 31)-1; vis[i] = 0; } vis[0] = 1; d[0] = 0; for (int i = 1; i < n; i++) d[i] = map[0][i]; /*PRIM body*/ for (int i = 1; i < n; i++) { int min = (1 << 31)-1, p = -1; for (int j = 0; j < n; j++)//get the minimal unvisited point if (!vis[j] && d[j] < min) { min = d[j]; p = j; } cost += d[p]; vis[p] = 1; for (int j = 0; j < n; j++)//update the info if (!vis[j] && map[p][j] < d[j]) d[j] = map[p][j]; } printf("%d/n", cost); return 0; }

 

题目二:Score inflation

算法:无限背包

 

题目三:Humble Numbers

题目描述:给定若干质因子,求出由这些质因子所能构成的所有整数中的第K大数是多少

算法:动态规划

1.最初的想法是用D[I]表示以第i个质数结尾当前以产生的最大质数,但是错的,会漏掉一些数;

2.后来看了MAIGO的代码,自己琢磨了两天明白了他的方法,用D[I]表示第i个质数当前用来产生新数的数在产生的数串中的位置,这样肯定不会漏;

#include <stdio.h> #include <string.h> const int maxn = 101; int n[maxn], m[maxn], h[100001]; int main(){ freopen("humble.in", "r", stdin); freopen("humble.out", "w", stdout); int N, K; scanf("%d %d", &N, &K); for (int i = 0; i < N; i++) scanf("%d", &n[i]); int last = 1; memset(h,0,sizeof(h)); memset(m,0,sizeof(m)); h[0] = 1; for (int i =1; i <= K; i++) { int min =(1 << 31)-1; for (int j = 0; j < N; j++) if (h[m[j]] * n[j] > h[i-1] && h[m[j]] * n[j] < min) min = n[j] * h[m[j]]; h[i] = min; for (int j = 0; j < N; j++) if (h[m[j]] * n[j] == min) m[j]++; } printf("%d/n", h[K]); return 0; }

 

题目四:Shaping Regions

题目大意:依次给定N个有色矩形,覆盖,求最后的图形中各种可见颜色的面积

算法:搜索

1.最开始的想法是从后往前枚举,然后与后面的图形进行切割,但是具体怎么切割不清楚;

2.看了MAIGO的代码,体会了一下,明白了;

3.与后面的切割时,先去处理X两边多出来的部分,重点是要更新X,然后在处理Y两边

 #include <string.h> #include <stdio.h> const int maxn = 1001; int n, x1[maxn], y1[maxn], x2[maxn], y2[maxn], color[maxn], area[maxn]; int now; void calc(int X1, int Y1, int X2, int Y2, int z) { while (z <= n && (X1 >= x2[z] || X2 <= x1[z] || Y1 >= y2[z] || Y2 <= y1[z]) ) z++; // find the nearest rec which has common area if (z > n) {//the final existing area which isn't covered by any rec put after it area[now] += (X2 - X1) * (Y2 - Y1); return; } if (X1 < x1[z]) { calc(X1, Y1, x1[z], Y2, z+1); X1 = x1[z];//cut the processed area } if (X2 > x2[z]) { calc(x2[z], Y1, X2, Y2, z+1); X2 = x2[z];//cut the processed area } if (Y1 < y1[z]) calc(X1, Y1, X2, y1[z], z+1);//as the coordinate X has been processed, Y is the factor only being dealt with if (Y2 > y2[z]) calc(X1, y2[z], X2, Y2, z+1); } int main(){ freopen("rect1.in", "r", stdin); freopen("rect1.out", "w", stdout); memset(x1,0,sizeof(x1)); memset(x2,0,sizeof(x2)); memset(y1,0,sizeof(y1)); memset(y2,0,sizeof(y2)); memset(area,0,sizeof(area)); scanf("%d %d %d/n", &x2[0], &y2[0], &n); color[0] = 1;// the empty color is numbered 1 for (int i = 1; i <= n; i++) scanf("%d %d %d %d %d/n", &x1[i], &y1[i], &x2[i], &y2[i], &color[i]); int maxcolor = 0; for (int i = n; i >= 0; i--) { now = color[i];//the color now is processed calc(x1[i], y1[i], x2[i], y2[i], i+1); if (now > maxcolor) maxcolor = now; } for (int i = 1; i <= maxcolor; i++) if (area[i] > 0) printf("%d %d/n", i, area[i]); return 0; }

 

题目五:Contact

题目大意:给定01串,求出长度为A-B之间各个01串的数量最多的前K组

算法:枚举+位处理

1.基本思路很清晰,一次枚举位,然后对新形成的串进行分类,但关键是怎么保存这个串以及怎么更新这个串;

数据结构:

01便想到位运算,把2进制转化为10进制,长度为L的01串前加个1便可区分不同长度的相同数值的01串;

更新:

   1.先去掉开头的1;  2.增加当前位的值;  3.把开头的数值用|运算强制转成1

对应的位运算分别为
s[j] &= (1 << L)-1

s[j] = (s[j] << 1) + ss[i] -'0';

s[j] |= (1 << j);

2.接下来的问题是输出格式的控制,建议先把所有有的串排个序,在分个类,出现相同数目的01串有几个,然后输出,容易控制;

 #include <stdio.h> #include <string.h> const int maxlen = 12, maxn = 1 << maxlen+1; int s[maxlen+1], d[maxlen+1], t[maxn], num[maxn], tt[52]; char ss[82]; void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void print(int num) { int len = 0; while (num > 0) { d[++len] = num % 2; num /= 2; } for (int i = len-1; i >= 1; i--) printf("%d", d[i]); } int main(){ freopen("contact.in", "r", stdin); freopen("contact.out", "w", stdout); int A, B, N; scanf("%d %d %d/n", &A, &B, &N); //infinitial the bit opration to 10..00 for (int i = A; i <= B; i++) s[i] = 1 << i; int len = 0; while (scanf("%s", ss) == 1) for (int i = 0; i < strlen(ss); i++) { len++; for (int j = A; j <= B; j++) { s[j] &= (1 << j)-1;//delete the head s[j] = (s[j] << 1) + ss[i] -'0';// insert the new digit s[j] |= (1 << j);//plus the head if (len >= j) t[s[j]]++;//new string is built } } //sort array t in discreased order int min = 1 << A, max = (1 << (B+1))-1; for (int i = min; i <= max; i++) num[i] = i;//num as the second key in increased order for (int i = 1, k = min;i <= max; i++, k++) for (int j = k+1; j <= max; j++) if (t[j] > t[k] || t[j] == t[k] && num[j] < num[k]) { swap(t[k], t[j]); swap(num[k], num[j]); } //output the string, first get tt[i] means the number of group int n = 0; for (int k = min; k <= max; k++) { if (t[k] == 0 || n > N) break; if (k == 1 || t[k] != t[k-1]) tt[++n]++; else tt[n]++; } if (n < N) N = n; n = min-1;//means the position for (int i = 1; i <= N; i++) { printf("%d/n", t[++n]); print(num[n]); for (int j = 2; j <= tt[i]; j++) {if (j % 6 != 1) printf(" ");print(num[++n]);if (j % 6 == 0 && j != tt[i]) printf("/n");} printf("/n"); } return 0; }

 

题目六:Stamps

题目大意:有若干种基础面值的邮票,每种数量无限,求最多用K张邮票不能拼出的最小数是多少

算法:BFS

1.在基础面值确定的条件下,每一张面值所需最少邮票数是这个面值的固有属性;
2.一开始想用DP,但方程找不出来,后来想到用BFS的向后拓展的功能可以实现,基于1,每个点最多被加入一次队列,空间用4B-INT型需要15M,所以改用4B-INT+2B-SHORT需11.5M,实际用了14M

#include <stdio.h> #include <string.h> const int maxvalue = 2000000, MAX = 1 << 15-1; short need[maxvalue+1], w[51]; int q[maxvalue+1]; int main(){ freopen("stamps.in", "r", stdin); freopen("stamps.out", "w", stdout); int n, k; //printf("%d/n", sizeof(short)); scanf("%d %d/n", &k, &n); for (int i = 0; i < n; i++) scanf("%d", &w[i]); for (int i = 0; i <= maxvalue; i++) need[i] = MAX; need[0] = 0;q[0] = 0; int head = 0, tail = 0; while (head <= tail) { if (need[q[head]] < k) for (int i = 0; i < n; i++) if (need[q[head]]+1 < need[q[head]+w[i]]) { tail++; q[tail] = q[head]+w[i]; need[q[tail]] = need[q[head]]+1; } head++; } int max = 0; while (need[++max] != MAX) {}; printf("%d/n", max-1); return 0; }

你可能感兴趣的:(USACO 3.1 分析)