EGOI #2 题解

EGOI #2

本次比赛鸣谢:宽宽yuzhouLi0204,还有我们敬爱的老师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 ].endw[ 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

  1. ( k ) 10 (k)_{10} (k)10 转化为二进制
  2. 如果是 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[jw[ 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[jw[ 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[ iw[ 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;
}

是个人都能看懂,灰常简单的题目啊!

谢谢大家对EGOI的支持!EGOI #3 定在6月26号下午进行,请大家积极参与!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值