AtCoder Beginner Contest 271 CDE题解

C

题目大意:
你有n本书,每本书有自己的序号,阅读规则是:从编号为1的书开始阅读,只能连续的阅读下去,也就是从1,2,3…等到 i , 同时你可以把自己未阅读的两本书换成一本任意编号的书,求你最多能读几本书。

思路:很明显二分答案即可,因为答案具有单调性。注意怎么统计即可。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=3e5+10;
const double eps=1e-4;

map<int,int> mp;
int a[N];
int n;
int get(int x)
{
	int ans=0,sum=0;
	for(auto v:mp){
		int aa=v.first,bb=v.second;
		if(aa<=x){
			ans++;
			sum+=bb-1;
		}
		else sum+=bb;
	}
	return ans+sum/2;
}
void work()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++;
	int l=n/2,r=n;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(get(mid)<mid) r=mid-1;
		else l=mid; 
	}
	cout<<r<<endl;
} 

signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	
	return 0;
}

D
题目大意:
有n张牌,第 i 张牌正面和反面分别写有数字 a i a_i ai b i b_i bi ,给定一个数值K,问这些牌的值加起来能否恰好等于K,每张牌可以使用正面的数字也可以使用反面数字。
如果可以,则输出每张牌的状态。

思路:很明显的dp问题,有点类似于背包,f[i][j]表示是否前 i 个数能恰好凑成 j
具体的转移方程为 :

if(f[i][j]) //如果f[i][j]能凑成,则
		if(j+a[i+1]<=s) f[i+1][j+a[i+1]]=1;
		if(j+b[i+1]<=s) f[i+1][j+b[i+1]]=1;

同时这道题还需要记录路径,注意一下如何记录和输出路径,其实就是倒着做状态转移。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
const int N=1e2+10,M=1e4+10;
const double eps=1e-4;

int f[N][M],a[N],b[N];
int n;
int st[N];
vector<char> ans;
void work()
{
	int n,s;
	cin>>n>>s;
	for(int i=0;i<=n;i++){
		for(int j=0;j<=s;j++) f[i][j]=0;
	}
	for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
	f[0][0]=1;
	for(int i=0;i<n;i++){
		for(int j=0;j<=s;j++){
			if(f[i][j]) {
				if(j+a[i+1]<=s) f[i+1][j+a[i+1]]=1;
				if(j+b[i+1]<=s) f[i+1][j+b[i+1]]=1;
			}
		}
	}
	
	if(f[n][s]) {
		cout<<"Yes"<<endl;
		for(int i=n;i>=1;i--){
			if(s>=a[i]&&f[i-1][s-a[i]]) {
				s-=a[i]; ans.push_back('H');
			}
			else {
				s-=b[i]; ans.push_back('T');
			}
		}
		reverse(ans.begin(),ans.end());
		for(auto i:ans) cout<<i;
		
	}
	else cout<<"No"<<endl;
} 

signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	
	return 0;
}

E
题目大意:给定m条路,给定一个序列E,求从点1到n的最短路,同时要满足选的道路的编号要时E的一个子序列。

思路: 可以发现可以直接在序列E上做dp,因为要满足选的编号是其子序列,有点类似拓扑排序。
dp[i]表示1到 i 的最短路。
转移为:

f[v[i]]=min(f[v[i]],f[u[i]],+w[i])

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
const int N=3e5+10;
const double eps=1e-4;
typedef pair<int,int> pii;

int f[N];
int a[N],b[N],c[N];
void work()
{
	int n,m,k;
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) f[i]=1e18;
	f[1]=0;
	for(int i=1;i<=m;i++) cin>>a[i]>>b[i]>>c[i];
	for(int i=1;i<=k;i++){
		int x; cin>>x;
		f[b[x]]=min(f[b[x]],f[a[x]]+c[x]);
	}
	if(f[n]==1e18) cout<<-1<<endl;
	else cout<<f[n]<<endl;
} 

signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值