链接: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了。代码如下:
/*
** ZOJ 3767 Elevator
** Created by Rayn @@ 2014/05/10
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main() {
int t;
scanf("%d", &t);
while(t--)
{
int n, m, sum = 0;
scanf("%d%d", &n, &m);
for (int i=0; i<n; i++)
{
int x;
scanf("%d", &x);
sum += x;
}
if (sum > m)
{
printf("Warning\n");
}
else
{
printf("Safe\n");
}
}
return 0;
}
【题意】给你一个整数N,然后将N表示为若干段1~K连续的数段的和,问你最小的段数,输出几段的K。
【思路】开始看这道题觉得是贪心或者DP吧,然后我想了一下觉得不会出现第四段,最后打了一下表发现果然最多只有三段。于是这样就可以暴力了。先把连续数段和处理出来,然后直接lower_bound查找第一段数,符合就直接输出,不行就枚举两段的和三段的。最后60MS过了,按道理来说复杂度很高的。下面是代码:
/*
** ZOJ 3768 Continuous Login
** Created by Rayn @@ 2014/05/10
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int a[50000], sz;
void init()
{
for (int i = 1; (i*(i+1)/2)<=123456789;i++)
{
a[i] = i*(i+1)/2;
sz = i;
}
sz++;
}
int main()
{
init();
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
int k = lower_bound(a+1,a+sz,n) - a;
int flag=0;
if (a[k]==n)
{
flag = 1;
printf("%d\n",k);
}
else
{
for (int i = 1; a[i] <= n; i++)
{
int l = n - a[i];
int j = lower_bound(a+1,a+sz,l) - a;
if (a[j]== l)
{
printf("%d %d\n",i,j);
flag = 1;
break;
}
}
}
if (!flag)
{
for (int i = 1; a[i]<= n; i++)
{
for (int j = 1; a[i]+a[j]<=n && !flag;j++)
{
int l = n - a[i] - a[j];
int k = lower_bound(a+1,a+sz,l) - a;
if (a[k] == l)
{
printf("%d %d %d\n",i,j,k);
flag = 1;
break;
}
}
}
}
}
return 0;
}
【题意】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"转移过来,因为这两个不能共存。
代码如下:
/*
** ZOJ 3769 Diablo III
** Created by Rayn @@ 2014/05/12
** 背包
*/
#include <cstdio>
#include <iostream>
#include <vector>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXM = 50000 + 10;
struct Equip {
int D, T;
Equip(int d = 0, int t = 0): D(d), T(t) {}
};
int dp[13][MAXM];
map<string, int> equip_id;
void Init()
{
equip_id["Finger"] = 0;
equip_id["Head"] = 1;
equip_id["Shoulder"] = 2;
equip_id["Neck"] = 3;
equip_id["Torso"] = 4;
equip_id["Hand"] = 5;
equip_id["Wrist"] = 6;
equip_id["Waist"] = 7;
equip_id["Legs"] = 8;
equip_id["Feet"] = 9;
equip_id["Shield"] = 10;
equip_id["Weapon"] = 11;
equip_id["Two-Handed"] = 12;
}
inline void takemax(int &a, int b)
{
if(a < b) a = b;
}
int main()
{
int T, N, M;
Init();
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &N, &M);
vector<Equip> E[13];
string s;
for(int i=0; i<N; ++i)
{
int d, t;
cin >> s >> d >> t;
E[equip_id[s]].push_back(Equip(d,t));
}
E[0].push_back(Equip(0, 0));
memset(dp, -1, sizeof(dp));
dp[0][0] = 0;
for(int i=0; i<(int)E[0].size(); ++i)
{
for(int j=0; j<i; ++j)
{
takemax(dp[0][min(E[0][i].T + E[0][j].T, M)], E[0][i].D + E[0][j].D);
}
}
int ans = -1;
for(int i=1; i<=12; ++i)
{
int p = (i == 12)? 9 : i - 1;
memcpy(dp[i], dp[p], sizeof(dp[p]));
for(int k=0; k<E[i].size(); ++k)
{
for(int j=0; j<=M; ++j)
{
if(dp[p][j] < 0)
continue;
takemax(dp[i][min(j + E[i][k].T, M)], dp[p][j] + E[i][k].D);
}
}
takemax(ans, dp[i][M]);
}
printf("%d\n", ans);
}
return 0;
}
【题意】给你一些人的ID,加入时间,积分。要求给这些人分配等级(LV1 ~ LV6,具体分配规则见题目)。
【思路】这道题也是一道大水题,就根据他的规则去模拟就行了,开始看了题之后就去敲,开始感觉自己写的很挫,还排了两次序,幸好最后1A了。下面是代码:
/*
** ZOJ 3770 Ranking System
** Created by Rayn @@ 2014/05/10
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX = 2005;
struct Member{
int num;
int ID, date, score;
int degree;
} man[MAX];
int cmp1(const Member& A, const Member &B)
{
if(A.score != B.score)
return A.score < B.score;
if(A.date != B.date)
return A.date > B.date;
else
return A.ID > B.ID;
}
int cmp2(const Member& A, const Member &B)
{
return A.num < B.num;
}
int main() {
#ifdef HotWhite
//freopen("in.txt", "r", stdin);
#endif
int T, n;
scanf("%d", &T);
while(T--)
{
memset(man, 0, sizeof(man));
scanf("%d", &n);
int cnt = 0;
for(int i=0; i<n; ++i)
{
int y,m,d;
scanf("%d %d/%d/%d %d", &man[i].ID, &y,&m,&d, &man[i].score);
man[i].num = i+1;
man[i].date = d + m*100 + y*10000;
if(man[i].score > 0)
cnt++;
}
sort(man, man+n, cmp1);
/*
for(int i=0; i<n; ++i)
{
printf("%d %d %d\n", man[i].ID, man[i].date, man[i].score);
}
//*/
int lv[7];
lv[1] = n - cnt;
lv[3] = (int)(cnt * 0.3);
lv[4] = (int)(cnt * 0.2);
lv[5] = (int)(cnt * 0.07);
lv[6] = (int)(cnt * 0.03);
lv[2] = cnt-lv[3]-lv[4]-lv[5]-lv[6];
for(int i=0, k=1; i<n; ++k)
{
for(int j=1; j<=lv[k]; ++j, ++i)
{
man[i].degree = k;
}
}
sort(man, man+n, cmp2);
for(int i=0; i<n; ++i)
{
printf("LV%d\n", man[i].degree);
}
}
return 0;
}
【题意】给你一个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)可以由下面的公式
然后用线段树维护这个矩阵,查询就很快速了。下面是代码:
/*
** ZOJ 3772 Calculate the Function
** Created by Rayn @@ 2014/05/13
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MOD 1000000007
using namespace std;
typedef long long LL;
const int MAXN = 100005;
struct Martix
{
LL arr[2][2];
Martix(int x11=0, int x12=0, int x21 =0, int x22=0)
{
arr[0][0] = x11; arr[0][1] = x12;
arr[1][0] = x21; arr[1][1] = x22;
}
Martix operator * (const Martix& b) const
{
Martix res(0, 0, 0, 0);
for(int i=0; i<2; ++i)
{
for(int j=0; j<2; ++j)
{
for(int k=0; k<2; ++k)
{
res.arr[i][j] += arr[i][k] * b.arr[k][j];
res.arr[i][j] %= MOD;
}
}
}
return res;
}
};
struct SegmentTree
{
int l, r;
Martix val;
} tree[MAXN<<2];
int A[MAXN];
void BuildTree(int l, int r, int root)
{
tree[root].l = l;
tree[root].r = r;
if(l == r)
{
tree[root].val = Martix(1, 1, A[l], 0);
return ;
}
int mid = (l + r) >> 1;
BuildTree(l, mid, root<<1);
BuildTree(mid+1, r, root<<1|1);
tree[root].val = tree[root<<1].val * tree[root<<1|1].val;
}
Martix Query(int left, int right, int root)
{
if(left <= tree[root].l && tree[root].r <= right)
{
return tree[root].val;
}
Martix res(1, 0, 0, 1);
int mid = (tree[root].l + tree[root].r) >> 1;
if(left <= mid)
res = res * Query(left, right, root<<1);
if(right > mid)
res = res * Query(left, right, root<<1|1);
return res;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int N, M;
scanf("%d%d", &N, &M);
for(int i=1; i<=N; ++i)
{
scanf("%d", &A[i]);
}
BuildTree(1, N, 1);
while(M--)
{
int L, R;
scanf("%d%d", &L, &R);
if(R - L <= 1)
{
printf("%d\n", A[R]);
}
else
{
Martix res = Query(L+2, R, 1);
LL ans = (A[L+1]*res.arr[0][0] + A[L]*res.arr[1][0]) % MOD;
printf("%lld\n", ans);
}
}
}
return 0;
}
【题意】?(>_o)! 是一种简单的语言,给你一段源代码,然后判断输出是否和源代码相同。
【思路】这道题说了很多废话,其实就是就两个地方需要判断,对于输出,存在"_"会输出源代码,"!"会输出"Hello, world!"。然后就判断一下是否和源代码相同即可。代码如下:
/*
** ZOJ 3775 ?(>_o)!
** Created by Rayn @@ 2014/05/10
*/
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;
bool Check(string& s)
{
string tmp = "";
for(int i=0; i<s.length(); ++i)
{
if(s[i] == '_')
tmp += s;
else if(s[i] == '!')
tmp += "Hello, world!";
if(tmp.length() > s.length())
return false;
}
return s == tmp;
}
int main()
{
int t;
string str;
scanf("%d%*c", &t);
while(t--)
{
getline(cin, str);
if(Check(str))
puts("Yes");
else
puts("No");
}
return 0;
}