EGOI #2
本次比赛鸣谢:宽宽, yuzhou,Li0204,还有我们敬爱的老师gutc对本场比赛的支持,谢谢!
{ \ }
下面就来给大家讲一讲这次的比赛
A:做题时间安排
思路:
个人认为本体还是较为简单的一道题。实际上就是一道01背包的模板并进行了一些些修改。先放上状态转移方程:
f
[
j
]
=
m
a
x
(
f
[
w
[
i
]
.
b
e
g
i
n
]
+
w
[
i
]
.
e
n
d
−
w
[
i
]
.
b
e
g
i
n
,
f
[
j
]
)
;
f[ \ j \ ] = max( \ f[ \ w[\ i \ ].begin \ ] + w[ \ i \ ].end - w[ \ i \ ].begin,f[ \ j \ ] \ );
f[ j ]=max( f[ w[ i ].begin ]+w[ i ].end−w[ i ].begin,f[ j ] );
意思就是放入背包和不放入背包的取max。是不是
s
o
e
a
s
y
?
so \ easy?
so easy?
代码里面也有一些注释,可以供大家理解
#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[1000005],n,m=-1;
struct node{
int begin;
int end;
}w[5010];//存储begin时间与end时间
bool cmp(node a,node b){
return(a.end<b.end)||((a.end==b.end)&&(a.begin<b.begin));
}//以末尾时间排序,否则以开头时间排序
signed main(){
cin>>n;
for (int i=1;i<=n;i++)
cin>>w[i].begin>>w[i].end;
sort(w+1,w+n+1,cmp);
for (int i=1;i<=n;i++)
for (int j=w[n].end;j>=w[i].end;j--)
if (j>=w[i].end)
f[j]=max(f[w[i].begin]+w[i].end-w[i].begin,f[j]);//放入背包与不放入背包取max
cout<<f[w[n].end]<<endl;
return 0;
}
{}
B:Lucky
思路:
其实我们可以模拟成二进制数的,即求第 k k k 个二进制数,只不过 0 0 0 是 4 4 4, 1 1 1 是 7 7 7。
- 将 ( k ) 10 (k)_{10} (k)10 转化为二进制
- 如果是
0
0
0 输出
4
4
4,是
1
1
1 输出
7
7
7,
比如 0100111 0100111 0100111 就输出 4744777 4744777 4744777
代码简单好写,就是思路比较难想:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int i, a[100000];
void binary (int x) {
while(x){
i++;
a[i] = x % 2;
x /= 2;
}
}
void ans (int k) {
binary(k++);
for(int i = j - 1; i >= 1; i--)
if(a[i] == 0)
puts("4");
else
puts("7");
}
signed main (){
int k;
cin >> k;
ans(k);
return 0;
}
{}
C:二八杠
在李行驭童鞋的强烈建议下,加上了这道水题,原因是善良的李行驭不愿让大家爆0,十分之仁慈
思路:
过于水的一道模拟题,如果您这一题都不会,那么请您要加强模拟题的训练。直接看代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a[105],b[105],c[105],d[105];
string ans[105];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i]>>c[i]>>d[i];
if( (a[i]==c[i]&&b[i]==d[i]) || (a[i]==d[i]&&b[i]==c[i]) )
ans[i]="tie";
else if( (a[i]==2&&b[i]==8) || (a[i]==8&&b[i]==2) )
ans[i]="first";
else if( (c[i]==2&&d[i]==8) || (c[i]==8&&d[i]==2) )
ans[i]="second";
else if(a[i]==b[i] && (c[i]!=d[i] || c[i]<a[i]) )
ans[i]="first";
else if(c[i]==d[i] && (a[i]!=b[i]||a[i]<c[i]) )
ans[i]="second";
else{
int firsts=(a[i]+b[i])%10;
int seconds=(c[i]+d[i])%10;
if(firsts>seconds)
ans[i]="first";
else if(firsts<seconds)
ans[i]="second";
else{
int maxf=max(a[i],b[i]);
int maxs=max(c[i],d[i]);
if(maxf>maxs)ans[i]="first";
else ans[i]="second";
}
}
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<endl;
return 0;
}
大家看一看就行了啊!(代码太乱,请见谅)
{}
D:Total points
思路:
这又是一道水题!
简单分析题目后,你就会发现这是一道十分之水的完全背包
请大家一定要分清楚完全背包和01背包的区别。
下面送给那些背包模板写不熟练的人:
01背包:
状态转移方程:
f
[
j
]
=
m
a
x
(
f
[
j
]
,
f
[
j
−
w
[
i
]
+
v
[
i
]
)
;
f[ \ j \ ] = max( \ f[ \ j \ ], \ f[j - w[ \ i \ ] + v[ \ i \ ] \ );
f[ j ]=max( f[ j ], f[j−w[ i ]+v[ i ] );
for(int i = 1; i <= n; i++)
for(int j = m; j >= w[i]; j--) //注意区别
f[j] = max(f[j], f[j - w[i]] + v[i]); //这里的w[i]指的是第i个物品的代价,v[i]指的是第i个物品的价值
完全背包:
状态转移方程:
f
[
j
]
=
m
a
x
(
f
[
j
]
,
f
[
j
−
w
[
i
]
+
v
[
i
]
)
;
f[ \ j \ ] = max( \ f[ \ j \ ], \ f[j - w[ \ i \ ] + v[ \ i \ ] \ );
f[ j ]=max( f[ j ], f[j−w[ i ]+v[ i ] );
for(int i = 1; i <= n; i++)
for(int j = w[i]; j <= m; j++) //注意区别
f[j] = max(f[j], f[j - w[i]] + v[i]); //这里的w[i]指的是第i个物品的代价,v[i]指的是第i个物品的价值
如果您这道题不会做,请您移步至 完全背包模板。
直接上代码:
#include<bits/stdc++.h>
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define set(a,b) memset(a,b,sizeof(a))
const int MAXN = 0x3f3f3f3f;
using namespace std;
int in(){ //快读 in()
int x=0,f=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
signed main(){
int n,m;
cin >> m >> n;
int v[n + 1];
int w[n + 1];
int f[m + 100];
mem(f);
for(int i=1; i<=n; i++)
cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++)
for(int j = w[i]; j <= m; j++)
f[j] = max(f[j], f[j - w[i]] + v[i]);
cout << f[m] << endl;
return 0;
}
有啥难的?就是送分好吧?
{}
E:Anger
思路:
很明显,如果要使这些剩下的数平方和最小,就是要尽量剔除最大的几个数。也就是让
n
n
n个数中的最大值最小,这是一个明显的贪心。
首先我想到了桶排序,用
f
i
f_i
fi 表示需求量为
i
i
i的人数,接下来只需要从
max
(
a
1
,
a
2
.
.
.
a
n
)
\max (a_1,a_2...a_n)
max(a1,a2...an)循环到1,把糖分给当前的的
f
i
f_i
fi,如果糖没了直接跳出循环。
#include <iostream>
using namespace std;
#define int long long
int f[99999999]; //这题数据手下留情了
int n,m;
int mx; //储存n个数中的最大值
int ans;
signed main(){
cin>>m>>n;
for(int i=1;i<=n;++i){
int tmp;cin>>tmp;
f[tmp]++;
mx=max(tmp,mx); //读入需求量,然后再把需求量为tmp的人数+1
}
for(int i=mx;i>=1;--i){ //一直循环到解决只需要一颗糖的的人
if(m>=f[i]){ //如果糖果够现在的人
m-=f[i]; //减去分掉的糖果
f[i-1]+=f[i]; //把糖果分给这f[i]个人,这f[i]个人需求量变为i-1
mx--; //最大需求量减一
if(!m)
break; //如果当前没糖果分了,就跳出循环
}
else if(m<f[i]){
f[i]-=m; //如果当前糖果不够现在的人,能给几个人是几个
f[i-1]+=m; //同理这些个人变为只需要i-1个糖果
break;
}
}
for(int i=mx;i>=1;--i) //从需求量最大的开始,平方后加到ans里
ans+=f[i]*i*i;
cout<<ans<<endl;;
return 0;
}
{}
F:Problems
在讲解法之前,先来膜拜一下 崔 真 言 崔真言 崔真言。
Orz %%%
思路:
灰常简单啊!
要注意的是这个可以找零钱是有无限张的,所以这是个完全背包!
我们设
f
i
f_{ \ i \ }
f i 表示价值为
i
{ \ i \ }
i 时,最多有多少种解。
则可以轻松得到状态转移方程
{ \ }
f
[
i
]
+
=
f
[
i
−
w
[
i
]
]
f [ \ i \ ] += f[ \ i - w[ \ i \ ] \ ]
f[ i ]+=f[ i−w[ i ] ]
然后就没有然后了
贴上代码:
#include<bits/stdc++.h>
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define set(a,b) memset(a,b,sizeof(a))
const int MAXN = 0x3f3f3f3f;
using namespace std;
int in(){ //快读 in()
int x=0,f=1;
char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return x*f;
} //可以不看哦!!
int m, n;
int w[1000005];
int f[1000005]; //背包
signed main(){
cin >> m >> n;
for(int i = 1; i <= n; i++)
cin >> w[i];
f[0] = 1;
for(int i = 1; i <= n; i++)
for(int j = w[i]; j <= m; j++)
f[j] += f[j - w[i]];
cout << f[m] << endl;
return 0;
}
H a p p y E n d i n g ! Happy \ Ending! Happy Ending!
{}
G:Group
这题也是出奇的简单
#include<bits/stdc++.h>
using namespace std;
int main (){
int m, n, k;
cin >> m >> n >> k;
int ans = 0;
while(m >= 2 && n >= 1 && m+n >= k+3){
ans++;
m -= 2;
n -= 1;
}
cout << ans << endl;
return 0;
}
是个人都能看懂,灰常简单的题目啊!