codeforces round 894(div.3)

Problem - A - Codeforces

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#include<vector>
#define endl '\n'
//#define int long long
using namespace std;
typedef long long ll;
const int N=25;
char s[N][N];
int n,m;
void solve() {
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>s[i][j];
        }
    }
    int cnt=0;
    string temp="vika";
    for(int j=1;j<=m;j++){
        for(int i=1;i<=n;i++){
            if(s[i][j]==temp[cnt]){
                cnt++;
                break;
            }
        }
    }
    if(cnt==4) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

Problem - B - Codeforces

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#include<vector>
#define endl '\n'
//#define int long long
using namespace std;
typedef long long ll;
const int N=2e5+10;
int b[N];
int n;
void solve() {
    cin>>n;
    vector<int>ans;
    for(int i=1; i<=n; i++) cin>>b[i];
    ans.push_back(b[1]);
    for(int i=2; i<=n; i++) {
        int x=b[i-1];
        if(b[i-1]<=b[i]&&b[i]<=b[i+1]) ans.push_back(b[i]);
        else {
            x--;
            while(x>b[i]) x--;
            if(x>0) ans.push_back(x);
            ans.push_back(b[i]);
        }
    }
    cout<<ans.size()<<endl;
    for(auto v:ans) cout<<v<<' ';
    cout<<endl;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int b[N];
int n;
void solve() {
	cin>>n;
	vector<int>ans;
	for(int i=1; i<=n; i++) cin>>b[i];
	ans.push_back(b[1]);
	for(int i=2; i<=n; i++) {
		if(b[i]>=b[i-1]) ans.push_back(b[i]);
		else {
			ans.push_back(b[i]);
			ans.push_back(b[i]);
		}
	}
	cout<<ans.size()<<endl;
	for(auto v:ans) {
		cout<<v<<' ';
	}
	cout<<endl;
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t=1;
	cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}

Problem - C - Codeforces

一开始都是一列一列竖着放置的,后将其横着放置(长的放在下面),问形状是否和之前一样

如果可行的话,变换后形状是不变的,那么一列一列看,变换后的第一列高度和之前一样,假设高度为a[1],那么至少得有a[1]列高度大于等于1才能拼成第一列,同理至少得有a[2]列高度大于等于2才能拼成第二列...

可以用树状数组来记录有几列高度是小于等于x的,即为sum(x),然后mp[x]记录有几列高度等于x,n-(sum(x)-mp[x])即为有几列高度大于等于x

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define endl '\n'
//#define int long long
using namespace std;
typedef long long ll;
const int N=2e5+10;
int a[N];
int tr[N];
int n;
//树状数组
int lowbit(int x) {
    return x & -x;
}
void add(int x,int c) {
    for(int i=x; i<=n; i+=lowbit(i)) tr[i]+=c;
}
int sum(int x) {
    int res=0;
    for(int i=x; i; i-=lowbit(i)) res+=tr[i];
    return res;
}
void solve() {
    cin>>n;
    map<int,int>mp;
    memset(tr,0,sizeof tr);
    for(int i=1; i<=n; i++) {
        cin>>a[i];
        mp[a[i]]++;//统计a[i]的个数
        add(a[i],1);
    }
    bool flag=true;
    for(int i=1; i<=n; i++) {
        if(n-(sum(i)-mp[i])<a[i]) {
            cout<<"No"<<endl;
            return;
        }
    }
    cout<<"Yes"<<endl;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

E.Kolya and Movie Theatre

一开始以为是dp,选与不选,然后选m个,但是状态转移不太好写

实际上这题是贪心

首先,假如我们选第一个,第三个,第五个,那么总共的娱乐值为a1+a3+a5-1*d-2*d-2*d=a1+a3+a5-5*d,所以我们将选的娱乐值全部加起来,然后看下标最大的是哪一个,记为i,再减去d*i即可

于是,我们可以贪心,将选择的放在优先队列中(肯定得放大于0的),然后当队列中的个数小于m时,就继续放,当队列中的个数等于m时,由于最大的下标一定,即减去的d*i一定,所以我们要使得放入队列的娱乐值这和最大,那么就要将队头(娱乐值最小的)和即将放入的比较,删去娱乐值最小的

AC代码: 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define endl '\n'
//#define int long long
using namespace std;
typedef long long ll;
const int N=2e5+10;
int a[N];
int n,m;
ll d;
void solve() {
    cin>>n>>m>>d;
    for(int i=1; i<=n; i++) cin>>a[i];
    priority_queue<int,vector<int>,greater<int>>q;
    ll sum=0;
    ll ans=0;
    for(int i=1; i<=n; i++) {
        if(a[i]<0) continue;
        if(q.size()==m&&q.top()<a[i]) {
            sum-=q.top();
            q.pop();
        }
        if(q.size()<m) {
            q.push(a[i]);
            sum+=a[i];
        }
        ans=max(ans,sum-d*i);
    }
    cout<<ans<<endl;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

Problem - D - Codeforces

组合数

首先,对于不同种的球,假设有x种球,那么组合数就是C(x,2),但是注意题目的要求是exactly,也就是说刚好n种

先二分找到第一个满足C(l,2)大于等于n的l,先特判如果C(l,2)刚好等于n,那么直接输出l

否则我们只能取l-1个不同种类的球,组合数就是C(l-1,2),然后补足剩下的,假设还差diff个,那么就在原来已经有的球中选diff种,每种球加一个,那么答案即为l-1+diff

可以保证,diff一定小于等于l-1,因为C(l-1,2)小于n,C(l,2)大于等于n,diff等于n-C(l-1,2),所以diff小于等于C(l,2-C(l-1,2),即l-1

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
#define endl '\n'
#define int long long
using namespace std;
typedef long long ll;
int n;
void solve() {
    cin>>n;
    int l=0,r=2e9;
    while(l<r){
        int mid=(l+r)/2;
        if(mid*(mid-1)/2>=n) r=mid;
        else l=mid+1;
    }
    if(l*(l-1)/2==n){
        cout<<l<<endl;
        return;
    }
    int diff=n-(l-1)*(l-2)/2;
    int res=l-1+diff;
    cout<<res<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0); 
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

Problem - F - Codeforces

原本是想用贪心的,优先杀死能量值大的怪兽,于是写下了以下代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define endl '\n'
#define int long long
using namespace std;
typedef long long ll;
int w,f;
int n;
void solve() {
    cin>>w>>f;
    cin>>n;
    priority_queue<int>q;
    for(int i=1; i<=n; i++) {
        int x;
        cin>>x;
        q.push(x);
    }
    //标记一下哪个增长的快
    int flag;
    if(w>f) flag=1;//sum1增长的快
    else flag=2;//sum2增长的快
    int cnt=0;//记录次数
    int sum1=0,sum2=0;


    while(q.size()) {
        sum1+=w,sum2+=f,cnt++;
        if(flag==1) {
            while(q.size()&&sum1>=q.top()) {
                sum1-=q.top();
                q.pop();
            }
            while(q.size()&&sum2>=q.top()) {
                sum2-=q.top();
                q.pop();
            }
        } else if(flag==2) {
            while(q.size()&&sum2>=q.top()) {
                sum2-=q.top();
                q.pop();
            }
            while(q.size()&&sum1>=q.top()) {
                sum1-=q.top();
                q.pop();
            }
        }
    }
    cout<<cnt<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

但是从第一个样例可以看成,似乎贪心并不可行,优先杀死大的并不能得到正确答案

解析:

怪兽的生命值的总和是一定的,我们选择一部分用水杀死,一部分用火杀死

然后具体哪些用水杀死,我们枚举所有的组合,相当于背包问题,选择哪些成为一个组合

假设生命值和为x用水杀死,生命值和为y用火杀死

那么次数即为max(x/w+(x%w!=0),y/f+(y%f!=0)),然后每次取min即可

法一:

枚举所有的组合可以用bitset,可以组合出的血量标记为1

首先,b[0]初始化为1,然后对于一个怪兽的生命值a,b|=b

由此,假设第一个怪兽的生命值为2,第二个怪兽的生命值为5,第三个怪兽的生命值为6,那么第0,2,5,6以及2+5,2+6,5+6位都标记为1

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<bitset>
#define endl '\n'
//#define int long long
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N];
int w,f;
int n;
void solve() {
    cin>>w>>f;
    cin>>n;
    bitset<N>bt;
    int sum=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    bt[0]=1;
    for(int i=1;i<=n;i++) bt|=bt<<a[i];
    int ans=2e9;
    for(int i=0;i<=sum;i++){
        if(!bt[i]) continue;
        int x=i/w+(i%w!=0);
        int y=(sum-i)/f+((sum-i)%f!=0);
        ans=min(ans,max(x,y));
    }
    cout<<ans<<endl;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

法二:

背包

dp[i][j]表示从前i个怪兽里选,总的生命值为j是否可行

for(int i=1;i<=n;i++){
    for(int j=a[i];j<=sum;j++){
        dp[i][j]|=dp[i-1][j-a[i]];
    }
}

然后由于dp[100][1e6]会爆空间,所以优化成一维 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define endl '\n'
//#define int long long
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N];
int dp[N];
int w,f;
int n;
void solve() {
    cin>>w>>f;
    cin>>n;
    int sum=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    memset(dp,0,sizeof dp);
    dp[0]=1;
    for(int i=1;i<=n;i++){
        for(int j=sum;j>=a[i];j--){
            dp[j]|=dp[j-a[i]];
        }
    }
    int ans=2e9;
    for(int i=0;i<=sum;i++){
        if(!dp[i]) continue;
        int x=i/w+(i%w!=0);
        int y=(sum-i)/f+((sum-i)%f!=0);
        ans=min(ans,max(x,y));
    }
    cout<<ans<<endl;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值