XJOI 奋斗群 群赛 13解题报告
原题
https://cn.vjudge.net/contest/186396 (Codeforces Round #214 (Div. 2))
A - Dima and Guards
题意
输入一个数n代表花的钱数,再输入四行数据,每一行有四个数字,代表:用巧克力贿赂第一个守卫的最小代价,用果汁贿赂第一个守卫的最小代价,用巧克力贿赂第二个守卫的最小代价,用果汁贿赂第二个守卫的最小代价,输出贿赂的一种方案。
题解
水题,数据比较小,直接模拟。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
scanf("%d",&n);
int a[5],b[5],c[5],d[5];
int cnt1[5],cnt2[5],cnt3[5],cnt4[5];
for(int i=1;i<=4;i++){
cin>>a[i]>>b[i]>>c[i]>>d[i];
}
for(int i=1;i<=4;i++){
cnt1[i]=a[i]+c[i];
if(cnt1[i]<=n) {
cout<<i<<" "<<a[i]<<" "<<n-a[i];
return 0;
}
cnt2[i]=a[i]+d[i];
if(cnt2[i]<=n) {
cout<<i<<" "<<a[i]<<" "<<n-a[i];
return 0;
}
cnt3[i]=b[i]+c[i];
if(cnt3[i]<=n) {
cout<<i<<" "<<b[i]<<" "<<n-b[i];
return 0;
}
cnt4[i]=b[i]+d[i];
if(cnt4[i]<=n) {
cout<<i<<" "<<b[i]<<" "<<n-b[i];
return 0;
}
}
cout<<-1;
}
B - Dima and To-do List
题意
给出n个事件和k,每个事件有一个权力值,k可以被n整除,如果取了第i个事件的权力值,那么下一个取的权力值将是第(i+k)个,问最小的权力值总和。
题解
由于k能被n整除,我们可以以i为索引处理输入的权力值的前缀(以余数判断),之后选取最小的即可,具体实现可以看代码。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100010;
const int inf=1e9;
int a,pre[MAXN]={0};
int main(){
int n,k,ans=0;
int min=inf;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&a);
if(i%k!=0){
pre[i%k]=pre[i%k]+a;
}
else pre[k]=pre[k]+a;
}
for(int i=1;i<=k;i++){
if(pre[i]<min){
ans=i;
min=pre[i];
}
}
cout<<ans;
}
C - Dima and Salad
题意
有n个水果,每个水果有自己的美味值和热量,要求在使所选的水果的美味值总和除以热量等于k的条件下计算出能取的最大美味值。
题解
变形一下题中所给的公式,我们就可以得到水果价值的计算方法,假设水果美味值为a,热量为b,那么他的价值v=a-b*k。有了这个计算方法,我们将其用动态规划进行状态转移,最后的dp[n][m]就是答案。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=110;
const int inf=1e9;
struct node{
int a;
int b;
}f[MAXN];
int dp[MAXN][MAXN*MAXN]={0};
int n,k,b,m;
/*
bool cmp(node a,node b){
if(a.a==b.a) return a.b<b.b;
else return a.a<b.a;
}
*/
void init(){
memset(dp,-10000,sizeof(dp));
dp[0][m]=0;
}
int main(){
scanf("%d%d",&n,&k);
m=n*100;
init();
for(int i=1;i<=n;i++){
scanf("%d",&f[i].a);
}
for(int i=1;i<=n;i++){
scanf("%d",&b);
f[i].b=f[i].a-b*k;
for(int j=m*2;j>=0;j--){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-f[i].b]+f[i].a);
}
}
if(dp[n][m]){
printf("%d",dp[n][m]);
}
else{
printf("-1");
}
return 0;
}
D - Dima and Trap Graph
题意
给出一张无向图,有n个结点和m条边(有重边与环),让你取一个最大的整数x使得x对于从结点1到n所经过每一条边i,有li<=x<=ri。、
题解
用并查集实现对于每一条边的左右段点的维护,由于不知道这里怎么二分,用了快排:按照右端点大小排序,把右端点大的拍到前面,这样排序以后,1和n连通的情况就是最优解,枚举m条边不断更新答案即可。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=3010;
const int inf=1e9;
int p[MAXN];
int n,m,ans;
struct edge{
int u,v,l,r;
}e[MAXN];
bool cmp(edge a,edge b){
if(a.r==b.r) return a.l<b.l;
return a.r>b.r;
}
int find(int n){
if(p[n]==n) return n;
else return p[n]=find(p[n]);
}
int main(){
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d %d %d %d",&e[i].u,&e[i].v,&e[i].l,&e[i].r);
}
sort(e,e+m,cmp);
for(int i=0;i<m;i++){
for(int j=1;j<=n;j++){
p[j]=j;
}
for(int j=0;j<m;j++){
if(e[j].l>e[i].l) continue;
if(e[j].r<e[i].l) continue;
p[find(e[j].u)]=find(e[j].v);
if(find(1)==find(n)){
ans=max(ans,e[j].r-e[i].l+1);
break;
}
}
}
if(ans!=0){
printf("%d",ans);
}
else{
printf("Nice work, Dima!");
}
return 0;
}
E - Dima and Magic Guitar
题意
有i*j个音符(数字),每个音符不大于k,有s个音符代表演奏的顺序,对于演奏顺序中的两个相邻音符,他们在矩阵中的曼哈顿距离即为这两个音符的复杂度,输出最大的复杂度。
题解
由于曼哈顿距离带有绝对值,对于每一个(x,y)只有四种情况:-x-y,-x+y,x-y,x+y,并且两点之间的最大距离肯定是这四种值的某两种之差,在此基础上有暴力去不断更新最大值。由于在输入矩阵中的音符时就可以做很多处理所以并不会超时。
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int num[100][4];
int main() {
int n,m,k,s,a,x,y,ans=-inf,i,j;
scanf("%d%d%d%d",&n,&m,&k,&s);
memset(num,1000,sizeof(num));
for(i=1; i<=n; i++) {
for(j=1; j<=m; j++) {
scanf("%d",&a);
num[a][0]=max(num[a][0],-i-j);
num[a][1]=max(num[a][1],-i+j);
num[a][2]=max(num[a][2],i-j);
num[a][3]=max(num[a][3],i+j);
}
}
scanf("%d",&x);
for(i=2; i<=s; i++) {
scanf("%d",&y);
for(j=0; j<=3; j++) {
ans=max(ans,num[x][j]+num[y][3-j]);
}
x=y;
}
printf("%d",ans);
}
总结
这次比赛的题目涉及的很广,动态规划、并查集、思维题都有,掌握的情况并不是很好,还要加油。