The 14th Zhejiang University Programming Contest(未完工)
链接:The 14th Zhejiang University Programming Contest
【2014/05/10】今下午和队友一起做了这套题,最后的Rank差不多这样,AC数目4道。现在就先把做了的4道题的心得写下来,剩下几道题以后会更新。
【2014/05/12】今天完成了Diablo III这道题,看了别人的解题报告后,真是自愧不如啊,dp果然是神奇的东西。
【2014/05/13】今天完成了Calcuate the Function这道题,矩阵真是强大,只怪线性代数学的撇。=_=#
这是一道最大的水题,zzy学长一看题就敲了,然后就pass了。代码如下:
1 /* 2 ** ZOJ 3767 Elevator 3 ** Created by Rayn @@ 2014/05/10 4 */ 5 #include <cstdio> 6 #include <cstring> 7 #include <algorithm> 8 using namespace std; 9 10 int main() { 11 12 int t; 13 scanf("%d", &t); 14 while(t--) 15 { 16 int n, m, sum = 0; 17 scanf("%d%d", &n, &m); 18 for (int i=0; i<n; i++) 19 { 20 int x; 21 scanf("%d", &x); 22 sum += x; 23 } 24 if (sum > m) 25 { 26 printf("Warning\n"); 27 } 28 else 29 { 30 printf("Safe\n"); 31 } 32 } 33 return 0; 34 }
【题意】给你一个整数N,然后将N表示为若干段1~K连续的数段的和,问你最小的段数,输出几段的K。
【思路】开始看这道题觉得是贪心或者DP吧,然后我想了一下觉得不会出现第四段,最后打了一下表发现果然最多只有三段。于是这样就可以暴力了。先把连续数段和处理出来,然后直接lower_bound查找第一段数,符合就直接输出,不行就枚举两段的和三段的。最后60MS过了,按道理来说复杂度很高的。下面是代码:
1 /* 2 ** ZOJ 3768 Continuous Login 3 ** Created by Rayn @@ 2014/05/10 4 */ 5 #include <cstdio> 6 #include <cstring> 7 #include <algorithm> 8 #define INF 0x3f3f3f3f 9 using namespace std; 10 int a[50000], sz; 11 12 void init() 13 { 14 for (int i = 1; (i*(i+1)/2)<=123456789;i++) 15 { 16 a[i] = i*(i+1)/2; 17 sz = i; 18 } 19 sz++; 20 } 21 22 int main() 23 { 24 init(); 25 int t; 26 scanf("%d",&t); 27 while(t--) 28 { 29 int n; 30 scanf("%d",&n); 31 int k = lower_bound(a+1,a+sz,n) - a; 32 int flag=0; 33 if (a[k]==n) 34 { 35 flag = 1; 36 printf("%d\n",k); 37 } 38 else 39 { 40 for (int i = 1; a[i] <= n; i++) 41 { 42 int l = n - a[i]; 43 int j = lower_bound(a+1,a+sz,l) - a; 44 if (a[j]== l) 45 { 46 printf("%d %d\n",i,j); 47 flag = 1; 48 break; 49 } 50 51 } 52 } 53 if (!flag) 54 { 55 for (int i = 1; a[i]<= n; i++) 56 { 57 for (int j = 1; a[i]+a[j]<=n && !flag;j++) 58 { 59 int l = n - a[i] - a[j]; 60 int k = lower_bound(a+1,a+sz,l) - a; 61 if (a[k] == l) 62 { 63 printf("%d %d %d\n",i,j,k); 64 flag = 1; 65 break; 66 } 67 } 68 } 69 } 70 } 71 return 0; 72 }
【题意】yuzhi在玩游戏"大菠萝3",有13个武器插槽,装备如下 {"Head", "Shoulder", "Neck", "Torso", "Hand", "Wrist", "Waist", "Legs", "Feet", "Finger", "Shield", "Weapon", "Two-Handed"},"Finger"可以戴两个,还有戴了"Two-Handed"就不能戴"Shield"和"Weapon"。然后每个武器都有两个属性: Damage和Toughness,要求在Toughness不小于M的情况下,怎样选择戴装备让Damage最大。
【思路】开始一直没有读懂题,其实这题就是一个包含特例的01背包问题,每个武器可以戴一个,关键是在于"Finger"可以戴两个,所以要先对"Finger"的状态进行处理。设置dp[i][j]代表佩戴前i件装备,Toughness为j的状况下最大的Damage。还有就是"Two-Handed"的状态不能从"Shield", "Weapon"转移过来,因为这两个不能共存。
代码如下:
1 /* 2 ** ZOJ 3769 Diablo III 3 ** Created by Rayn @@ 2014/05/12 4 ** 背包 5 */ 6 #include <cstdio> 7 #include <iostream> 8 #include <vector> 9 #include <map> 10 #include <cstring> 11 #include <algorithm> 12 using namespace std; 13 const int MAXM = 50000 + 10; 14 15 struct Equip { 16 int D, T; 17 Equip(int d = 0, int t = 0): D(d), T(t) {} 18 }; 19 int dp[13][MAXM]; 20 map<string, int> equip_id; 21 22 void Init() 23 { 24 equip_id["Finger"] = 0; 25 equip_id["Head"] = 1; 26 equip_id["Shoulder"] = 2; 27 equip_id["Neck"] = 3; 28 equip_id["Torso"] = 4; 29 equip_id["Hand"] = 5; 30 equip_id["Wrist"] = 6; 31 equip_id["Waist"] = 7; 32 equip_id["Legs"] = 8; 33 equip_id["Feet"] = 9; 34 equip_id["Shield"] = 10; 35 equip_id["Weapon"] = 11; 36 equip_id["Two-Handed"] = 12; 37 } 38 inline void takemax(int &a, int b) 39 { 40 if(a < b) a = b; 41 } 42 int main() 43 { 44 int T, N, M; 45 46 Init(); 47 scanf("%d", &T); 48 while(T--) 49 { 50 scanf("%d%d", &N, &M); 51 52 vector<Equip> E[13]; 53 string s; 54 for(int i=0; i<N; ++i) 55 { 56 int d, t; 57 cin >> s >> d >> t; 58 E[equip_id[s]].push_back(Equip(d,t)); 59 } 60 E[0].push_back(Equip(0, 0)); 61 memset(dp, -1, sizeof(dp)); 62 dp[0][0] = 0; 63 for(int i=0; i<(int)E[0].size(); ++i) 64 { 65 for(int j=0; j<i; ++j) 66 { 67 takemax(dp[0][min(E[0][i].T + E[0][j].T, M)], E[0][i].D + E[0][j].D); 68 } 69 } 70 int ans = -1; 71 for(int i=1; i<=12; ++i) 72 { 73 int p = (i == 12)? 9 : i - 1; 74 memcpy(dp[i], dp[p], sizeof(dp[p])); 75 for(int k=0; k<E[i].size(); ++k) 76 { 77 for(int j=0; j<=M; ++j) 78 { 79 if(dp[p][j] < 0) 80 continue; 81 takemax(dp[i][min(j + E[i][k].T, M)], dp[p][j] + E[i][k].D); 82 } 83 } 84 takemax(ans, dp[i][M]); 85 } 86 printf("%d\n", ans); 87 } 88 return 0; 89 }
【题意】给你一些人的ID,加入时间,积分。要求给这些人分配等级(LV1 ~ LV6,具体分配规则见题目)。
【思路】这道题也是一道大水题,就根据他的规则去模拟就行了,开始看了题之后就去敲,开始感觉自己写的很挫,还排了两次序,幸好最后1A了。下面是代码:
1 /* 2 ** ZOJ 3770 Ranking System 3 ** Created by Rayn @@ 2014/05/10 4 */ 5 #include <cstdio> 6 #include <cstring> 7 #include <algorithm> 8 using namespace std; 9 const int MAX = 2005; 10 11 struct Member{ 12 int num; 13 int ID, date, score; 14 int degree; 15 } man[MAX]; 16 17 int cmp1(const Member& A, const Member &B) 18 { 19 if(A.score != B.score) 20 return A.score < B.score; 21 if(A.date != B.date) 22 return A.date > B.date; 23 else 24 return A.ID > B.ID; 25 } 26 int cmp2(const Member& A, const Member &B) 27 { 28 return A.num < B.num; 29 } 30 int main() { 31 32 #ifdef HotWhite 33 //freopen("in.txt", "r", stdin); 34 #endif 35 36 int T, n; 37 scanf("%d", &T); 38 while(T--) 39 { 40 memset(man, 0, sizeof(man)); 41 scanf("%d", &n); 42 int cnt = 0; 43 for(int i=0; i<n; ++i) 44 { 45 int y,m,d; 46 scanf("%d %d/%d/%d %d", &man[i].ID, &y,&m,&d, &man[i].score); 47 man[i].num = i+1; 48 man[i].date = d + m*100 + y*10000; 49 if(man[i].score > 0) 50 cnt++; 51 } 52 sort(man, man+n, cmp1); 53 /* 54 for(int i=0; i<n; ++i) 55 { 56 printf("%d %d %d\n", man[i].ID, man[i].date, man[i].score); 57 } 58 //*/ 59 int lv[7]; 60 lv[1] = n - cnt; 61 lv[3] = (int)(cnt * 0.3); 62 lv[4] = (int)(cnt * 0.2); 63 lv[5] = (int)(cnt * 0.07); 64 lv[6] = (int)(cnt * 0.03); 65 lv[2] = cnt-lv[3]-lv[4]-lv[5]-lv[6]; 66 for(int i=0, k=1; i<n; ++k) 67 { 68 for(int j=1; j<=lv[k]; ++j, ++i) 69 { 70 man[i].degree = k; 71 } 72 } 73 sort(man, man+n, cmp2); 74 for(int i=0; i<n; ++i) 75 { 76 printf("LV%d\n", man[i].degree); 77 } 78 } 79 return 0; 80 }
【题意】给你一个n个数的序列 Ai,定义一个[L,R]的查询,求Fi(R)的值。
- Fi (Li) = ALi ;
- Fi (Li + 1) = A(Li + 1);
- for all x >= Li + 2, Fi (x) = Fi (x – 1) + Fi (x – 2) × Ax;
【思路】由上面的公式,对于每一个Ai可以构造出,则Fi(R)可以由下面的公式
然后用线段树维护这个矩阵,查询就很快速了。下面是代码:
1 /* 2 ** ZOJ 3772 Calculate the Function 3 ** Created by Rayn @@ 2014/05/13 4 */ 5 #include <cstdio> 6 #include <cstring> 7 #include <algorithm> 8 #define MOD 1000000007 9 using namespace std; 10 typedef long long LL; 11 const int MAXN = 100005; 12 13 struct Martix 14 { 15 LL arr[2][2]; 16 Martix(int x11=0, int x12=0, int x21 =0, int x22=0) 17 { 18 arr[0][0] = x11; arr[0][1] = x12; 19 arr[1][0] = x21; arr[1][1] = x22; 20 } 21 Martix operator * (const Martix& b) const 22 { 23 Martix res(0, 0, 0, 0); 24 for(int i=0; i<2; ++i) 25 { 26 for(int j=0; j<2; ++j) 27 { 28 for(int k=0; k<2; ++k) 29 { 30 res.arr[i][j] += arr[i][k] * b.arr[k][j]; 31 res.arr[i][j] %= MOD; 32 } 33 } 34 } 35 return res; 36 } 37 }; 38 struct SegmentTree 39 { 40 int l, r; 41 Martix val; 42 } tree[MAXN<<2]; 43 44 int A[MAXN]; 45 46 void BuildTree(int l, int r, int root) 47 { 48 tree[root].l = l; 49 tree[root].r = r; 50 if(l == r) 51 { 52 tree[root].val = Martix(1, 1, A[l], 0); 53 return ; 54 } 55 int mid = (l + r) >> 1; 56 BuildTree(l, mid, root<<1); 57 BuildTree(mid+1, r, root<<1|1); 58 tree[root].val = tree[root<<1].val * tree[root<<1|1].val; 59 } 60 Martix Query(int left, int right, int root) 61 { 62 if(left <= tree[root].l && tree[root].r <= right) 63 { 64 return tree[root].val; 65 } 66 Martix res(1, 0, 0, 1); 67 int mid = (tree[root].l + tree[root].r) >> 1; 68 if(left <= mid) 69 res = res * Query(left, right, root<<1); 70 if(right > mid) 71 res = res * Query(left, right, root<<1|1); 72 return res; 73 } 74 int main() 75 { 76 int T; 77 scanf("%d", &T); 78 while(T--) 79 { 80 int N, M; 81 scanf("%d%d", &N, &M); 82 for(int i=1; i<=N; ++i) 83 { 84 scanf("%d", &A[i]); 85 } 86 BuildTree(1, N, 1); 87 while(M--) 88 { 89 int L, R; 90 scanf("%d%d", &L, &R); 91 if(R - L <= 1) 92 { 93 printf("%d\n", A[R]); 94 } 95 else 96 { 97 Martix res = Query(L+2, R, 1); 98 LL ans = (A[L+1]*res.arr[0][0] + A[L]*res.arr[1][0]) % MOD; 99 printf("%lld\n", ans); 100 } 101 } 102 103 } 104 return 0; 105 }
【题意】?(>_o)! 是一种简单的语言,给你一段源代码,然后判断输出是否和源代码相同。
【思路】这道题说了很多废话,其实就是就两个地方需要判断,对于输出,存在"_"会输出源代码,"!"会输出"Hello, world!"。然后就判断一下是否和源代码相同即可。代码如下:
1 /* 2 ** ZOJ 3775 ?(>_o)! 3 ** Created by Rayn @@ 2014/05/10 4 */ 5 #include <iostream> 6 #include <cstdio> 7 #include <string> 8 #include <algorithm> 9 using namespace std; 10 11 bool Check(string& s) 12 { 13 string tmp = ""; 14 for(int i=0; i<s.length(); ++i) 15 { 16 if(s[i] == '_') 17 tmp += s; 18 else if(s[i] == '!') 19 tmp += "Hello, world!"; 20 if(tmp.length() > s.length()) 21 return false; 22 } 23 return s == tmp; 24 } 25 int main() 26 { 27 int t; 28 string str; 29 30 scanf("%d%*c", &t); 31 while(t--) 32 { 33 getline(cin, str); 34 if(Check(str)) 35 puts("Yes"); 36 else 37 puts("No"); 38 } 39 return 0; 40 }