APIO模拟赛(HGOI20180909)
想法:贪心。
A.最大高度大的先剪
首先需要知道:
1.每个草最多剪1次
假设有个草剪了2次,显然可以放到最后一次剪得效果和剪2次的效果一样的,
为了少剪那么草最多剪去一次,从而,步数step>N就是impossible!
所以枚举步数,每次从后往前贪心取最大的高度,后剪
code 70pts:
# include <bits/stdc++.h>
using namespace std;
const int MAXN=105;
bool cut[MAXN];
int g[MAXN],h[MAXN],r[MAXN],u[MAXN],n,H;
bool check(int step)
{
memcpy(r,h,sizeof(h));
u[0]=0;
for (int i=1;i<=n;i++) r[i]+=step*g[i];
memset(cut,false,sizeof(cut));
for (int i=1;i<=step;i++) {
int MAX=0,P;
for (int j=1;j<=n;j++)
if (r[j]>MAX&&cut[j]==false) MAX=r[j],P=j;
u[++u[0]]=P;
cut[P]=true;
}
for (int i=1;i<=step/2;i++) swap(u[i],u[step-i+1]);
memcpy(r,h,sizeof(h));
for (int i=1;i<=step;i++) {
for (int j=1;j<=n;j++) r[j]+=g[j];
r[u[i]]=0;
int sum=0;
for (int j=1;j<=n;j++) sum+=r[j];
if (sum<=H) return true;
}
int sum=0;
for (int i=1;i<=n;i++) sum+=r[i];
if (sum<=H) return true;
else return false;
}
int main()
{
freopen("grass.in","r",stdin);
freopen("grass.out","w",stdout);
int T;scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&H);
for (int i=1;i<=n;i++) scanf("%d",&h[i]);
for (int i=1;i<=n;i++) scanf("%d",&g[i]);
int ans=-1;
for (int i=0;i<=n;i++)
if (check(i)) { ans=i; break; }
printf("%d\n",ans);
}
return 0;
}
反例:
input:
3 6
12 1 1
1 4 4
output:
3
WA:
-1
100pts DP!!!
就是找出这样2条规律:
2.长得快的最后剪
这是显然的,如果长得快的先剪那么在后面剪一次长得慢的时候长得快的还在生长,
F[i][j]前i棵草,剪去j棵(此时过了j天),最大剪去高度
F[i][j]转移:
第i棵草剪去,F[i-1][j-1]+h[i] +g[i]*j //i-1棵剪去j-1棵加上这时候草的高度先长后剪H‘ = h[i]+g[i]*j
第i棵草不剪,F[i-1][j] 没有多余动作
方程: f[i][j]=max(f[i-1][j],f[i-1][j-1]+h[i]+g[i]*j);
AC code
# include <bits/stdc++.h>
# define Rint register int
using namespace std;
const int MAXN=105;
int h[MAXN],g[MAXN],f[MAXN][MAXN];
int read()
{
int r=1,num=0; char ch=getchar();
if (ch=='-') ch=getchar(),r=-1;
while (isdigit(ch)) num=num*10+ch-'0',ch=getchar();
return num*r;
}
int main()
{
freopen("grass.in","r",stdin);
freopen("grass.out","w",stdout);
int T=read();
while (T--) {
int n=read(),H=read();
int sumh=0,sumg=0;
for (Rint i=1;i<=n;i++) h[i]=read(),sumh+=h[i];
for (Rint i=1;i<=n;i++) g[i]=read(),sumg+=g[i];
memset(f,0,sizeof(f));
for (Rint i=1;i<=n-1;i++)
for (Rint j=i+1;j<=n;j++)
if (g[i]>g[j]) swap(g[i],g[j]),swap(h[i],h[j]);
for (Rint i=1;i<=n;i++)
for (Rint j=1;j<=i;j++)
f[i][j]=max(f[i-1][j],f[i-1][j-1]+h[i]+g[i]*j);
int ans=-1;
for (Rint i=0;i<=n;i++)
if (sumh+sumg*i-f[n][i]<=H) { ans=i; break; }
printf("%d\n",ans);
}
return 0;
}
Hint:题目出错:最后的条件改为A[i]-x<=B[j]<=A[i]+y;
想法:
50pts二分图匹配,按照题意模拟建边,匈牙利算法O(n2)轻松跑过
100pts运用单调性,知道A,B两个序列都是有序的,而x和y都是定值那么
满足的条件: A[i](单调递增) B[j](单调递增)
当B[j]不满足A[i]限制1的时候( B[j]>A[i]+y )B[j+1] 到B[m]都不符合
当B[j]不满足A[i]限制2的时候(B[j]<A[i]-x) B[1]到B[j]都不符合
那么贪心取最小的A[i]和B[j]就行
code 100pts (匈牙利和单调队列放一起了凑合着看看)
# include <bits/stdc++.h> # define Rint register int using namespace std; const int MAXN=1005,MAXN2=100005; bool mp[MAXN][MAXN],vis[MAXN]; int a[MAXN2],b[MAXN2],pre[MAXN]; int n,m,x,y,ans=0; bool find(int u) { for (Rint v=1;v<=m;v++) if (mp[u][v]&& !vis[v]) { vis[v]=1; if (pre[v]==-1||find(pre[v])) { pre[v]=u; return 1; } } return 0; } void solve() { memset(pre,-1,sizeof(pre)); for (Rint i=1;i<=n;i++) { memset(vis,false,sizeof(vis)); if (find(i)) ans++; } } void work() { for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=m;i++) scanf("%d",&b[i]); int i=1,j=1,ans=0; while (true) { while (b[j]<a[i]-x&&j<=m) j++; if (i>n||j>m) break; if (b[j]<=a[i]+y) ans++,j++; if (i>n||j>m) break; i++; } printf("%d\n",ans); } int main() { freopen("match.in","r",stdin); freopen("match.out","w",stdout); scanf("%d%d%d%d",&n,&m,&x,&y); if (n>MAXN) { work();return 0; } for (Rint i=1;i<=n;i++) scanf("%d",&a[i]); for (Rint i=1;i<=m;i++) scanf("%d",&b[i]); memset(mp,sizeof(mp),false); for (Rint i=1;i<=n;i++) for (Rint j=1;j<=m;j++) if (a[i]-x<=b[j]&&b[j]<=a[i]+y) mp[i][j]=true; solve(); printf("%d\n",ans); return 0; }
【APIO2013】hahahahaha做什么?
#include<cstdio> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; const int N = 101010, M = 301010, K = 22; int n, m, k, cn = 0; long long ans = 0, maxL, ssize; int cost[N], father[N], color[N]; long long size[N] = {0}; long long get_max(long long a, long long b) { if (a > b) return a; else return b; } long long get_min(long long a, long long b) { if (a < b) return a; else return b; } class Road { public : int x, y, w, flag, ans; Road (int x, int y, int w = 0, int flag = 0) : x(x), y(y), w(w), flag(flag) {} }; Road * road[M], * road_sp[K]; class Node; class Edge { public : Node * target; Edge * next; int flag; /*Edge(Node * target, Edge * next, int flag) : target(target), next(next), flag(flag) {}; ~Edge() { if (next) delete next; }*/ }; class Node { public : Edge * head; Node * father; long long size, deep, flag; /* Node (long long size) : size(size), head(0), father(0), deep(0), flag(0) {} ~Node() { if (head) delete head; } */ }; Node * node[K]; Node * my_node[1010]; int my_node_count; Edge * my_edge[1010]; int my_edge_count; Node * get_node(long long size) { Node * now = my_node[my_node_count ++]; now->size = size; now->head = 0; now->father = 0; now->deep = 0; now->flag = 0; return now; } Edge * get_edge(Node * target, Edge * next, int flag) { Edge * now = my_edge[my_edge_count ++]; now->target = target; now->next = next; now->flag = flag; return now; } void newedge(Node * x, Node * y, int flag) { Edge * e1 = get_edge(y, x->head, flag), * e2 = get_edge(x, y->head, flag); x->head = e1; y->head = e2; } bool cmp_w(const Road * a, const Road * b) {return a->w < b->w;} void init() { for (int i=0; i<1010; i++) { my_edge[i] = new Edge(); my_node[i] = new Node(); } scanf("%d%d%d", &n, &m, &k); int x, y, w; for (int i=0; i<m; i++) { scanf("%d%d%d", &x, &y, &w); road[i] = new Road(x, y, w); } for (int i=0; i<k; i++) { scanf("%d%d", &x, &y); road_sp[i] = new Road(x, y); } for (int i=1; i<=n; i++) scanf("%d", cost+i); } int get_father(int x) { int head = x; while (father[head] != head) head = father[head]; while (x != head) { int tmp = father[x]; father[x] = head; x = tmp; } return head; } void select() { sort(road, road+m, cmp_w); for (int i=1; i<=n; i++) father[i] = i; for (int i=0; i<m; i++) { int x = get_father(road[i]->x), y = get_father(road[i]->y); if (x != y) { road[i]->flag = 1; father[x] = y; } } m = 0; for (int i=0; m<n-1; i++) if (road[i]->flag) road[m++] = road[i]; } void check_select() { printf("m = %d\n", m); for (int i=0; i<m; i++) printf("x = %d y = %d w = %d\n", road[i]->x, road[i]->y, road[i]->w); } void build() { for (int i=1; i<=n; i++) father[i] = i; for (int i=0; i<k; i++) { int x = get_father(road_sp[i]->x), y = get_father(road_sp[i]->y); if (x != y) father[x] = y; } for (int i=0; i<m; i++) { int x = get_father(road[i]->x), y = get_father(road[i]->y); if (x != y) father[x] = y; else road[i]->flag = 0; } for (int i=1; i<=n; i++) father[i] = i; for (int i=0; i<m; i++) if (road[i]->flag) { int x = get_father(road[i]->x), y = get_father(road[i]->y); father[x] = y; } for (int i=1; i<=n; i++) if (father[i] == i) color[i] = cn++; for (int i=1; i<=n; i++) { color[i] = color[get_father(i)]; size[color[i]] += cost[i]; } m = 0; for (int i=0; i<n-1; i++) if (!road[i]->flag) road[m++] = road[i]; } void check_build() { for (int i=0; i<cn; i++) { printf("Color %d :", i); for (int j=1; j<=n; j++) if (color[j] == i) printf("%d ", j); printf(" size = %I64d\n", size[i]); } printf("sp edge:\n"); for (int i=0; i<k; i++) printf("x = %d y = %d\n", color[road_sp[i]->x], color[road_sp[i]->y]); printf("other edge:\n"); for (int i=0; i<m; i++) printf("x = %d y = %d w = %d\n", color[road[i]->x], color[road[i]->y], road[i]->w); } long long work(Node * & x, int w) { long long s = 0; if (x->flag) { //road_sp[x->flag-1]->ans = w; s = x->size * w; x->flag = 0; ssize -= x->size; } x = x->father; return s; } void dfs(Node * now) { for (Edge * e = now->head; e!=0; e=e->next) { if (!e->target->deep) { e->target->deep = now->deep + 1; e->target->flag = e->flag; e->target->father = now; dfs(e->target); if (e->flag) ssize += e->target->size; now->size += e->target->size; } } } void clear_road_sp() { for (int i=0; i<k; i++) road_sp[i]->ans = 9999999; } void print_road_sp(long long s) { return ; printf("%I64d ", s); for (int i=0; i<k; i++) printf("%d ", road_sp[i]->ans); printf("\n", s); } long long get_ans() { my_node_count = 0; my_edge_count = 0; for (int i=0; i<cn; i++) node[i] = get_node(size[i]); for (int i=0; i<cn; i++) father[i] = i; for (int i=0; i<k; i++) if (road_sp[i]->flag) { int x = get_father(color[road_sp[i]->x]), y = get_father(color[road_sp[i]->y]); if (x == y) return 0; father[x] = y; newedge(node[color[road_sp[i]->x]], node[color[road_sp[i]->y]], i+1); } maxL = 0; for (int i=0; i<m; i++) { int x = get_father(color[road[i]->x]), y = get_father(color[road[i]->y]); if (x != y) { road[i]->flag = 0; father[x] = y; newedge(node[color[road[i]->x]], node[color[road[i]->y]], 0); } else { if (road[i]->w > maxL) maxL = road[i]->w; road[i]->flag = 1; } } node[color[1]]->deep = 1; ssize = 0; dfs(node[color[1]]); long long s = 0; //clear_road_sp(); for (int i=0; i<m; i++) { if (ssize * maxL + s < ans) break; if (road[i]->flag) { Node * x = node[color[road[i]->x]], * y = node[color[road[i]->y]]; int c = 0; while (x != y) if (x->deep > y->deep) s += work(x, road[i]->w); else s += work(y, road[i]->w); } } //if (ssize != 0) printf("A = %I64d\n", ssize); //print_road_sp(s); for (int i=1; i<=cn; i++) delete node[i]; return s; } void dfs(int now) { if (now == k) { ans = get_max(get_ans(), ans); return ; } road_sp[now]->flag = 1; dfs(now+1); road_sp[now]->flag = 0; dfs(now+1); } int main() { freopen("toll.in", "r", stdin); freopen("toll.out", "w", stdout); init(); select(); //check_select(); build(); //check_build(); dfs(0); cout << ans << endl; return 0; }