说好的的赛区难度... →_→ “不完全是赛区难度”==“完全不是赛区难度”
看完了题做好了爆0的准备,从testC开始写,结果有30,正解是dp...
testB我是在最后十分钟才写的,感觉像最长公共子序列,但是时间不够了,我就直接printf("%d\n", s/e);结果有80...
testA写了个Floyd,然后就开始乱搞了,结果就wa0...
-----------------------------------------------------------------------------
testA
输入文件: testA.in 输出文件: testA.out 时限: 2000ms
问题描述:
有一个城市拥有N个节点,被M条有权无向路径连接。现在你要在一个地方(可以在路径上当然也可以在节点上)开设一家咖啡馆,使得所有节点达到这个咖啡馆的最短路里面最大值最小(也就是说离咖啡馆最远的节点距离尽可能得小),求出这个最大值的最小值。
输入描述:
第一行N和M。
第2至M+1行,每行三个整数U,V,W。表示从U到V有一条权值为W的无向边。
输出描述:
一行一个数表示答案。 四舍五入至2位小数
数据范围 N<=200 , W<=100000 , M<=19900
样例输入:
3 2
1 2 100
2 3 1
样例输出:
50.50
题解:
二分答案L
对于一个L,枚举每一条边(x, y, w),只要有一条边符合情况,L合法
对于一条边,枚举每一个点k:(1)dis[x][k]>L && dis[y][k]>L ->这条边不和发
l = L-dis[x][k], r = L-dis[y][k]
(2) l>=w || r>=w || r+l>=w ->这个点对于这条边合法
(3) 求一个区间, 既对于点k在这条直线上标出不合法的安放区间
-> 求并集U:
若U==[0,w]->此边不合法
else ->合法
1 #define NOMBRE "testA"
2 #include <cstdio>
3 #include <vector>
4 #include <cstring>
5 #include <algorithm>
6 using namespace std;
7
8 const int INF = 0x3f3f3f3f;
9 const int MAXN = 200+10;
10
11 double Pri;
12 int N, M, dis[MAXN][MAXN];
13
14 struct Edge{
15 int to, d;
16 };
17
18 vector< pair<int, int> > cj;
19 vector<Edge> e[MAXN];
20
21 inline Edge MakeEdge(int to, int d){
22 Edge ret;
23 ret.to = to, ret.d = d;
24 return ret;
25 }
26
27 inline void Floyd(){
28 for (int i=1; i<=N; i++)
29 dis[i][i] = 0;
30 for (int k=1; k<=N; k++)
31 for (int i=1; i<=N; i++){
32 if (dis[i][k]==INF) continue;
33 for (int j=1; j<=N; j++)
34 dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
35
36 }
37 }
38
39 inline bool OkayEdge(int L, int x, int y, int w){
40 int dl, dr, Tail = 0, CJs;
41 cj.clear();
42 for (int k=1; k<=N; k++){
43 dl = L-dis[x][k], dr = L-dis[y][k];
44 if (dl<0 && dr<0) return false;
45 if (dl>=w || dr>=w || dl+dr>=w) continue;
46 cj.push_back(make_pair(dl+1, w-dr-1));
47 }
48 if (cj.empty()) return true;
49 sort(cj.begin(), cj.end());
50 CJs = cj.size();
51 for (int i=0; i<CJs; i++){
52 if (Tail<cj[i].first) return true;
53 if (Tail<cj[i].second+1) Tail = cj[i].second+1;
54 }
55 return false;
56 }
57
58 inline bool Okay(int L){
59 int Es;
60 for (int i=1; i<=N; i++){
61 Es = e[i].size();
62 for (int j=0; j<Es; j++)
63 if (OkayEdge(L, i, e[i][j].to, e[i][j].d)) return true;
64 }
65 return false;
66 }
67
68 int main(){
69 freopen(NOMBRE ".in", "r", stdin);
70 freopen(NOMBRE ".out", "w", stdout);
71
72 memset(dis, 0x3f, sizeof(dis));
73 scanf("%d %d", &N, &M);
74 int u, v, d;
75 for (int i=0; i<M; i++)
76 scanf("%d %d %d", &u, &v, &d),
77 d <<= 1, e[u].push_back(MakeEdge(v, d)),
78 dis[u][v] = dis[v][u] = min(dis[u][v], d);
79 Floyd();
80 int l = 0, r = INF, Mid;
81 while (l<=r){
82 Mid = (l+r)>>1;
83 if (Okay(Mid)) r = Mid-1, Pri = Mid;
84 else l = Mid+1;
85 }
86 printf("%.2lf", Pri/=2.0);
87 }
-----------------------------------------------------------------------------
testB
输入文件: testB.in 输出文件testB.out 时限2000ms
问题描述:
方师傅有两个由数字组成的串 a1,a2,⋯,an 和 b1,b2,⋯,bm。 有一天,方师傅感到十分
无聊因此他决定用这两个串来玩玩游戏。游戏规则十分简单,方师傅会进行一些操作,每个操作可能是以下两种操作之一:
1.从a串选择一个a的非空前缀,再从b串选一个b的非空前缀。这两个前缀的最后一个元素必须相等,完成选择后把这两个前缀删除。
2.删除两个串所有的元素。
第一种操作会耗费e的能量值,并为方师傅增加一美分到他的电子账户中。第二种操作会耗费两个串的不完整度的能量。不完整度 = 两个串已经被删除的元素的数目。只有执行第二种操作后,方师傅才能从电子帐户中取出他的钱。
刚开始时,方师傅有一个空的电子账户和s的能量,请问方师傅最多可以赚多少美分?注意,由于乐警官偷吃光了方师傅的士力架,导致方师傅无法补充能量,因此方师傅的能量任何时候都不能小于0。
输入描述:
第一行4个整数,n,m,s,e(1≤n,m≤10^5;1≤s≤3×10^5;10^3≤e≤10^4)。
第二行n个整数,a1,a2⋯an.
第三行m个整数,b1,b2⋯bm.
1≤ai,bi≤10^5
输出描述:
输出一个整数,方师傅可以最多赚得的美分数目。
样例输入1:
5 5 100000 1000
1 2 3 4 5
3 2 4 5 1
样例输出1:
3
样例输入2:
3 4 3006 1000
1 2 3
1 2 4 3
样例输出2:
2
dp[i][j]表示A匹配到i位置获得j美分是匹配到B的位置
//看来标程才想到vector...多么美好vector啊
1 #define NOMBRE "testB"
2 #include <vector>
3 #include <cstdio>
4 #include <cstring>
5 #include <algorithm>
6 using namespace std;
7
8 const int MAXN = 1e5+10;
9 const int MAXJ = 300+10;
10
11 int n, m, s, e, x, Best, Pri, a[MAXN], b[MAXN], dp[MAXN][MAXJ];
12 vector<int> v[MAXN];
13
14 int main(){
15 freopen(NOMBRE ".in", "r", stdin);
16 freopen(NOMBRE ".out", "w", stdout);
17
18 memset(dp, 0x3f, sizeof(dp));
19 scanf("%d %d %d %d", &n, &m, &s, &e), Best = s/e;
20 for (int i=1; i<=n; i++)
21 scanf("%d", &a[i]);
22 for (int i=1; i<=m; i++)
23 scanf("%d", &b[i]),
24 v[b[i]].push_back(i);
25
26 int temp;
27 dp[0][0] = 0;
28 for (int i=1; i<=n; i++){
29 dp[i][0] = 0;
30 for (int j=0; j<=Best; j++){
31 dp[i][j] = dp[i-1][j];
32 x = upper_bound(v[a[i]].begin(), v[a[i]].end(), dp[i-1][j-1])-v[a[i]].begin();
33 temp = v[a[i]].size();
34 if (x<temp) dp[i][j] = min(dp[i][j], v[a[i]][x]);
35 if (j>Pri && i+dp[i][j]+j*e<=s) Pri = j;
36 }
37 }
38 printf("%d\n", Pri);
39 }
-----------------------------------------------------------------------------
testC
输入文件: testC.in 输出文件testC.out 时限1000ms
问题描述:
给你一组数,a1, a2, a3,⋯,an。令:G=gcd(a1, a2, a3,⋯,an)。
现在从中任意删除一些数字,设剩下的数为:a1, a2, a3,⋯,am。再令:g=gcd(a1, a2, a3,⋯,am)
现要求G=g,问最多能删除多少数?
输入描述:
第一行一个数n,第二行n个数a1,a2,a3,⋯,an。
1≤n≤700
1≤ai≤10000
输出描述:
输出只有一个数,表示最多能删除多少数。
样例输入:
3
4 6 8
样例输出
1
题解:
dp[i]表示使得gcd为i最少使用多少数
1 #define NOMBRE "testc"
2 #include <cstdio>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 const int N = 10000;
8 const int MAXN = 700+10;
9
10 int n, g, dp[N+10], a[MAXN], max;
11
12 int gcd(int a, int b){
13 if (!a && b) return b;
14 if (!b && a) return a;
15 while (b^=a^=b^=a%=b);
16 return a;
17 }
18
19 int main(){
20 freopen(NOMBRE ".in", "r", stdin);
21 freopen(NOMBRE ".out", "w", stdout);
22
23 memset(dp, 0x3f, sizeof(dp));
24 scanf("%d", &n), dp[0] = Max = g = 0;
25 for (int i=0; i<n; i++)
26 scanf("%d", &a[i]), g = gcd(g, a[i]), Max = max(Max, a[i]);
27 for (int i=0; i<n; i++)
28 for (int j=0; j<=Max; j++)
29 dp[gcd(a[i], j)] = min(dp[gcd(a[i], j)], dp[j]+1);
30 printf("%d\n", n-dp[g]);
31 }