嗯……第一场当时还不知道报名,第二场报上了,拿了250/400,果然分如其人……
这里包括的是第一场的ABC题,第二场的AB题的题解在另一篇(这些都是自己AC了的),第一场的D和第二场的C至今仍然在WA,不知道怎么回事,到时候也讲讲思路求指点吧。
A. Magic Box
这道题并不难做,唯一需要注意的是cx,cy和cz,以及任意两个量的差之间是没有前后关系的(这个事情样例里也告诉你了),所以比较简单的方法就是先把cx,cy,cz排序,然后根据输入串一个一个模拟,然后计算出两两之间的差,排序,比对就行了……
#include<iostream>
#include<math.h>
#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
char str[30000];
int c[10];
int cnt[200];
int dif[10];
int main()
{
cin>>c[1]>>c[2]>>c[3];
cin>>str;
int len = strlen(str);
sort(&c[1], &c[4]);
int i;
int sum=0;
int ans = 0;
for(i=0;i<len;i++)
{
cnt[str[i]]++;
sum++;
ans = max(ans, sum);
dif[1] = abs(cnt[‘R‘] - cnt[‘Y‘]);
dif[2] = abs(cnt[‘Y‘] - cnt[‘B‘]);
dif[3] = abs(cnt[‘B‘] - cnt[‘R‘]);
sort(&dif[1], &dif[4]);
if(dif[1] == c[1] && dif[2] == c[2] && dif[3] == c[3])
{
memset(cnt, 0, sizeof(cnt));
sum=0;
}
}
cout<<ans<<endl;
//system("pause");
return 0;
}
B. Professor Q’s Software
这道题如果你注意到以下几点, 就并不难了:
- 每个模块只会等一个信号,也就是说当模块发出一个信号的时候,我就能知道他激活了哪个模块,所以这很显然是一个图的关系,如果模块A能够放出一个信号1,模块B就是等信号1的,那么就可以画一条A-B的边。
- 更关键的事情在于,你会发现等待相同信号的模块被激活的次数是一样的(如果模块A和模块B都等待1号信号,那么来一个信号的时候他们一定同时被激活),这也就是说等待信号相同的点是完全可以合并的,反正他们的答案是一样的;
- 所以我们就可以用信号来命名节点了,这样就变成了一个很容易的拓扑图DP问题,因为题目明确说了不会有循环激活,意思就是这个图里不会有环。DP方程是:
f[i]=Σ(f[k])+base[i]
,其中k代表所有和直接指向i的节点。意思就是每个节点的激活数量就是所有能激活他的节点的激活数量,加上最初的那组信号激活他的数量。
由于每个点只有3条边,所以这个图十分稀疏,O(n+e)可以轻松搞定。
- 最后一个问题就是信号都是int,太大了存不下,但是只有10w个点,也就是说那么多数里面只有10w个有用的,那我们离散化一下就好了。这样,问题就全部解决了。
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct etype
{
int num;
int next;
};
etype node[1000000];
int p_nodes=0;
int edge[200000];
int t;
int n,m;
int base[200000];
int dp[200000];
int numbers[1000000];
int p_numbers=0;
int init_nums[600000];
struct Input
{
int w;
int cnt;
int release[10];
};
Input input[200000];
int node_num[200000];
const int MOD = 142857;
void init()
{
memset(node, 0, sizeof(node));
p_nodes=0;
memset(edge, 0, sizeof(edge));
memset(base,0,sizeof(base));
memset(dp, -1, sizeof(dp));
memset(numbers, 0, sizeof(numbers));
p_numbers=0;
}
int go(int now)
{
if(dp[now]!=-1)
{
return dp[now];
}
dp[now] = base[now];
int e;
for(e=edge[now]; e!=0; e=node[e].next)
{
dp[now] = (dp[now] + go(node[e].num))%MOD;
}
return dp[now];
}
void new_edge(int k1, int k2)
{
p_nodes++;
node[p_nodes].num=k2;
node[p_nodes].next = edge[k1];
edge[k1] = p_nodes;
return;
}
int find(int tar)
{
int low=1, high=p_numbers;
while(low<=high)
{
int mid=(low+high)/2;
if(numbers[mid] == tar)
return mid;
if(numbers[mid] < tar)
low = mid+1;
else
high = mid-1;
}
return -1;
}
void push(int x)
{
numbers[++p_numbers] = x;
return;
}
int main()
{
scanf("%d", &t);
for(int files=1; files<=t; files++)
{
init();
scanf("%d %d", &n, &m);
int i, j;
for(i=1; i<=m; i++)
{
scanf("%d", &init_nums[i]);
push(init_nums[i]);
}
for(i=1; i<=n; i++)
{
scanf("%d", &input[i].w);
push(input[i].w);
scanf("%d", &input[i].cnt);
for(j=1; j<=input[i].cnt; j++)
{
scanf("%d", &input[i].release[j]);
push(input[i].release[j]);
}
}
sort(&numbers[1], &numbers[p_numbers+1]);
p_numbers = unique(&numbers[1], &numbers[p_numbers+1]) - numbers - 1;
for(i=1; i<=m; i++)
{
int mirror = find(init_nums[i]);
base[mirror]++;
}
for(i=1; i<=n; i++)
{
node_num[i] = find(input[i].w);
}
for(i=1; i<=n; i++)
{
int now_node = node_num[i];
for(j=1; j<=input[i].cnt; j++)
{
int now_target = find(input[i].release[j]);
new_edge(now_target, now_node);
}
}
for(i=1; i<=n;i++)
{
int now_node = node_num[i];
if(dp[now_node]==-1)
{
dp[now_node] = go(now_node);
}
}
for(i=1;i<=n;i++)
{
int now_node = node_num[i];
printf("%d", dp[now_node]);
if(i!=n)
printf(" ");
}
printf("\n");
}
//system("pause");
return 0;
}
C. Island Travel
这题我一开始没做出来,经过别人提示才会做的。一开始总是想各种排序之后怎么搞……
其实很简单,40%的数据只要裸Dijkstra就行了,但是你会发现其中很多边是没用的,事实上对于一个点来说,只有它的四个邻居,也就是他上下左右最近的那个点对他有意义。更远的点一定可以先走到你的邻居,再由你的邻居走到他,这样一定不会更差。
所以,虽然有10w个点,但是每个点只需要建4条边,一个十分稀疏的图,用SPFA或者是堆优化Dijkstra就能轻松搞了。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
typedef pair<LL, int> P;
const LL INF = 1e18;
int n;
struct etype
{
int num;
int len;
int next;
};
etype node[1000000];
int p_nodes=0;
int edge[200000];
LL dis[200000];
priority_queue< P, vector<P>, greater<P> > q;
struct ptype
{
int num;
LL x;
LL y;
};
ptype point[200000];
bool cmp1(const ptype &a, const ptype &b)
{
return a.x<b.x;
}
bool cmp2(const ptype &a, const ptype &b )
{
return a.y<b.y;
}
void new_edge(int k1, int k2, LL len)
{
p_nodes++;
node[p_nodes].num = k2;
node[p_nodes].len = len;
node[p_nodes].next = edge[k1];
edge[k1] = p_nodes;
return;
}
int main()
{
scanf("%d", &n);
int i;
for(i=1; i<=n; i++)
{
scanf("%lld %lld", &point[i].x, &point[i].y);
point[i].num=i;
}
for(i=1;i<=n;i++)
dis[i] = INF;
sort(&point[1], &point[n+1], cmp1);
for(i=1; i<=n;i++)
{
if(i+1<=n)
{
new_edge(point[i].num, point[i+1].num,
min(abs(point[i].x - point[i+1].x), abs(point[i].y - point[i+1].y)));
}
if(i-1>=1)
{
new_edge(point[i].num, point[i-1].num,
min(abs(point[i].x - point[i-1].x), abs(point[i].y - point[i-1].y)));
}
}
sort(&point[1], &point[n+1], cmp2);
for(i=1; i<=n;i++)
{
if(i+1<=n)
{
new_edge(point[i].num, point[i+1].num,
min(abs(point[i].x - point[i+1].x), abs(point[i].y - point[i+1].y)));
}
if(i-1>=1)
{
new_edge(point[i].num, point[i-1].num,
min(abs(point[i].x - point[i-1].x), abs(point[i].y - point[i-1].y)));
}
}
dis[1]=0;
q.push(make_pair(0,1));
while(!q.empty())
{
P now_pair = q.top();
q.pop();
int now = now_pair.second;
if(dis[now] < now_pair.first)
continue;
int e = edge[now];
for(e; e!=0; e=node[e].next)
{
if(dis[now] + node[e].len < dis[node[e].num])
{
dis[node[e].num] = dis[now] + node[e].len;
q.push(make_pair(dis[node[e].num], node[e].num));
}
}
}
printf("%lld\n", dis[n]);
//system("pause");
return 0;
}
D. Recruitment
本题依然在WA,我的思路是这样的:
对男女分开DP,DP方程是
dp[i][rest][restbudget]=max(dp[i?1][rest?1][restbudget?v[i]],dp[i?1][rest][restbudget])
意思就是每个人可以选或者不选,当然要控制一下条件什么的,对于所有不合法的状态都赋成-INF。
之后枚举给两边各自多少预算,由于之前已经求出了dp[n][rest][budget], 所以直接就可以知道给x预算的时候两边能选出多少value,计算和即可;
字典序的问题是这样解决的:用choice[i][rest][budget]表示面对这个情况的时候选没选第i个人,如果选了是1,不选是0,当选与不选的结果相等的时候,尽量不选,因为我们要把机会尽量留给前面的人;之后分配预算的时候,男女分别利用choice来找到自己选了哪些人,挑出来以后排个序,如果比当前最优解字典序更小,则取代之。
不知道什么地方有错,自己的数据和同学的数据都过了……
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,limit[2],b;
int dp[2][110][110][1100];
int choice[2][110][110][1100];
const int INF = 1<<30;
struct etype
{
int gender;
int value;
int cost;
int num;
};
etype ori[200], after[2][200];
int p[2];
int bestorder[200]={0};
int noworder[200]={0};
int bottom_cost[2];
bool cmp(const etype &a, const etype &b)
{
return a.cost < b.cost;
}
void calc_bottomcost()
{
etype tmp[2][200];
memcpy(tmp, after, sizeof(after));
int nowgender;
for(nowgender=0; nowgender<=1; nowgender++)
{
sort(&tmp[nowgender][1], &tmp[nowgender][p[nowgender]+1], cmp);
int i;
for(i=1; i<=limit[nowgender]; i++)
{
bottom_cost[nowgender] += tmp[nowgender][i].cost;
}
}
return;
}
void generate_order(int b0, int b1)
{
int i,j;
int p_noworder=0;
for(int nowgender=0; nowgender<=1; nowgender++)
{
int now_x = limit[nowgender];
int now_b = (nowgender == 0)? b0:b1;
for(i=p[nowgender]; i>=1; i--)
{
if(choice[nowgender][i][now_x][now_b] == 1)
{
now_x --;
now_b -= after[nowgender][i].cost;
noworder[++p_noworder] = after[nowgender][i].num;
}
}
}
sort(&noworder[1], &noworder[p_noworder+1]);
return;
}
bool check()
{
int i;
for(i=1; i<=limit[0] + limit[1]; i++)
{
if(noworder[i] < bestorder[i])
return true;
else if(noworder[i] > bestorder[i])
return false;
}
return false;
}
int main()
{
scanf("%d %d %d %d", &n, &limit[0], &limit[1], &b);
int i;
for(i=1; i<=n;i++)
{
char tmp;
scanf("\n%c %d %d", &tmp, &ori[i].value, &ori[i].cost);
if(tmp == ‘M‘)
{
ori[i].gender =0;
}
else
{
ori[i].gender=1;
}
ori[i].num=i;
after[ori[i].gender][++p[ori[i].gender]] = ori[i];
}
calc_bottomcost();
memset(dp,0,sizeof(dp));
int j,k;
for(int nowgender=0; nowgender<=1; nowgender++)
{
for(i=1; i<=p[nowgender];i++)
{
for(j=0; j<=limit[nowgender]; j++)
{
for(k=0; k<=b; k++)
{
if(i<j)
{
dp[nowgender][i][j][k] = -INF;
continue;
}
int c1 = dp[nowgender][i-1][j][k];
int c2=-INF;
if(j-1 >= 0 && k - after[nowgender][i].cost >=0)
{
c2 = dp[nowgender][i-1][j-1][k-after[nowgender][i].cost] + after[nowgender][i].value;
}
//cout<<"nowgender "<<nowgender<<" i "<<i<<" j "<<j <<" k "<<k;
dp[nowgender][i][j][k] = max(c1,c2);
//cout<<" "<<dp[nowgender][i][j][k]<<"\n";
if(c1>=c2)
choice[nowgender][i][j][k] = 0;
else
choice[nowgender][i][j][k] = 1;
//cout<<"nowgender "<<nowgender<<" i "<<i<<" j "<<j <<" k "<<k;
//cout<<" "<<choice[nowgender][i][j][k]<<"\n";
}
}
}
}
int max_value=0;
int min_budget=INF;
for(i=bottom_cost[0]; i<=b; i++)
{
for(j=bottom_cost[1]; j<=b-i; j++)
{
int now_value = dp[0][p[0]][limit[0]][i] + dp[1][p[1]][limit[1]][j];
if(now_value > max_value)
{
max_value = now_value;
min_budget = i+j;
generate_order(i,j);
memcpy(bestorder, noworder, sizeof(bestorder));
}
else if(now_value == max_value && i+j < min_budget)
{
min_budget = i+j;
generate_order(i,j);
memcpy(bestorder, noworder, sizeof(bestorder));
}
else if(now_value == max_value && i+j == min_budget)
{
generate_order(i,j);
if(check())
{
memcpy(bestorder, noworder, sizeof(bestorder));
}
}
}
}
printf("%d %d\n", max_value, min_budget);
for(i=1;i<=limit[0]+limit[1];i++)
{
printf("%d", bestorder[i]);
if(i<limit[0]+limit[1])
printf(" ");
}
printf("\n");
system("pause");
return 0;
}