AtCoder Beginner Contest 245
题意:给出序列 A , B A,B A,B, 找出满足下列条件的序列 X X X是否存在:
- X i = A i 或 X i = B i X_i=A_i或X_i=B_i Xi=Ai或Xi=Bi , X X X的长度为 N N N.
- ∣ X i − X i + 1 ∣ ≤ K |X_i-X_{i+1}|\leq K ∣Xi−Xi+1∣≤K ,对于 1 ≤ i ≤ N − 1 1\leq i \leq N-1 1≤i≤N−1.
考虑dp, 定义 d p [ i ] [ 0 ] 为选了 i 个数,且第 i 个数为 A 中元素, d p [ i ] [ 1 ] 为第 i 个数为 B 中元素 定义dp[i][0]为选了i个数,且第i个数为A中元素,dp[i][1]为第i个数为B中元素 定义dp[i][0]为选了i个数,且第i个数为A中元素,dp[i][1]为第i个数为B中元素。
d p 数组的值表示是否可以达成 dp数组的值表示是否可以达成 dp数组的值表示是否可以达成。
然后根据题目的条件来进行转移即可。
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
typedef pair<int,int> pii;
//head
const int N=2e5+10,mod=998244353;
int a[N],b[N];
int dp[N][2];
void work()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
}
dp[1][0]=dp[1][1]=1;
for(int i=2;i<=n;i++){
if(abs(a[i]-b[i-1])<=k) dp[i][0]|=dp[i-1][1];
if(abs(a[i]-a[i-1])<=k) dp[i][0]|=dp[i-1][0];
if(abs(b[i]-a[i-1])<=k) dp[i][1]|=dp[i-1][0];
if(abs(b[i]-b[i-1])<=k) dp[i][1]|=dp[i-1][1];
}
if(dp[n][0]||dp[n][1]) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
signed main()
{
int t;
t=1;
while(t--) work();
return 0;
}
题意:给出两个多项式, A , C A,C A,C, C 是由 A ∗ B 得到的 C是由A*B得到的 C是由A∗B得到的,现在让你求出B的每一项的系数。
1 ≤ N ≤ 100 1\leq N \leq 100 1≤N≤100.
根据题意,我们可以直接来模拟两个多项式相乘即可,由于数据范围很小,因此可以暴力的做,需要注意的是要从最后一项往前面推,因为题目保证了 A N ! = 0 A_N!=0 AN!=0, 一开始我从前往后推出现了RE,因为除以0所以会出现错误。
下标的关系可以纸上模拟一下就出来了。
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
typedef pair<int,int> pii;
//head
const int N=2e3+10,mod=998244353;
int a[N],c[N];
int b[N];
void work()
{
int n,m;
cin>>n>>m;
for(int i=0;i<=n;i++) cin>>a[i];
for(int i=0;i<=n+m;i++) cin>>c[i];
b[m]=c[n+m]/a[n];
for(int i=m-1;i>=0;i--){
int sum=0;
int cnt=0;
for(int j=n-1;j>=n-(m-1-i)-1;j--){
cnt++;
sum+=b[i+cnt]*a[j];
}
b[i]=(c[i+n]-sum)/a[n];
}
for(int i=0;i<=m;i++) cout<<b[i]<<" ";
cout<<endl;
}
signed main()
{
int t;
t=1;
while(t--) work();
return 0;
}
题意:有 N N N个物品, M M M个箱子,物品和箱子都有长度和宽度。问能否让这些物品成功的放入箱子里,一个箱子最多放一个物品,且物体不能旋转。
首先想到的是贪心的放,对于某个物品来说,我们要尽量放到小的箱子里,考虑长和宽两个因素比较困难,因此我们可以对其按长排序,只考虑长,若不存在某个宽度满足条件,则说明无法放入。
那要如何来贪心呢?考虑先把小的箱子放进去,这样会有什么问题呢?若后面有很多箱子可以放,就会导致决策出现问题:例如若当前两个物品长和宽为 2 , 3 和 3 , 4 2,3 和 3,4 2,3和3,4 , 箱子为 3 , 4 和 5 , 3 和 6 , 3 3,4和5,3和6,3 3,4和5,3和6,3, 若按我们贪心,要把2,3放进3,4,然后会发现3,4放不进去了,因此这样无法保证结果的正确性。 那么从后往前考虑贪心,先把大的放进去,为什么这样是对的?因为我们先放大的,所以放大的决策不会影响放小的选择,大的能够放进去,则就判断下一个能否放入,否则就不能放入。
在具体的实现中,可以使用 m u l t i s e t multiset multiset来维护箱子的宽度,然后使用双指针来模拟遍历物品和箱子。
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
typedef pair<int,int> pii;
//head
const int N=4e5+10,mod=998244353;
pii a[N],b[N];
multiset<int> s;
void work()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) {
int x; cin>>x;
a[i].x=x;
}
for(int i=1;i<=n;i++) {
int x; cin>>x;
a[i].y=x;
}
for(int i=1;i<=m;i++) {
int x; cin>>x;
b[i].x=x;
}
for(int i=1;i<=m;i++) {
int x; cin>>x;
b[i].y=x;
}
bool f=1;
sort(a+1,a+1+n); sort(b+1,b+1+m);
for(int i=n,j=m;i>=1;i--){
while(j>=1&&a[i].x<=b[j].x){
s.insert(b[j].y);
j--;
}
auto id=s.lower_bound(a[i].y);
if(id==s.end()) {
f=0; break;
}
else s.erase(id);
}
if(f) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
signed main()
{
ios;
int t;
t=1;
while(t--) work();
return 0;
}
题意:给定一个有向图,问有从多少个点出发可以无限的走下去。
首先容易想到,所有能够走到环上的点和组成环的点的数量和即是答案。
那么如何去统计这些点的数量呢? 并不是很好统计,(好像用tanjan缩点也可以统计),正难则反,我们考虑有多少点无法一直走下去。
其实此时就可以想到反向建边了,在反向建边的情况下,考虑有多少个点可以走到环,则这些点实际上就是无法走到环的点。
用拓扑排序就可以很好的解决这个问题,有多少点能够入队,那么这些点就是实际上无法走到环的点。
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
typedef pair<int,int> pii;
//head
const int N=2e5+10,mod=998244353;
int e[N],ne[N],h[N],idx;
int in[N];
int n,m,ans;
void add(int a,int b)
{
e[idx]=b; ne[idx]=h[a]; h[a]=idx++;
}
void tp()
{
queue<int> q;
for(int i=1;i<=n;i++){
if(in[i]==0) q.push(i);
}
while(q.size()){
int t=q.front(); q.pop();
ans--;
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(--in[j]==0) q.push(j);
}
}
}
void work()
{
/*
拓扑排序判环:若所有点都能入队,则其不存在环,若无法全部入队,则一定存在环(环之后的点无法遍历到)
本题我们要找的是有多少点能够走到环的地方,不如反向思考,从环能够走到哪些点,这时就可以反向建边
进行拓扑排序,这样我们无法遍历到的点实际上就是答案(有多少点能走到环上)
可以遍历的点就是那些无法走到环上的点
*/
memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
add(b,a); //反向建边
in[a]++;
}
ans=n;
tp();
cout<<ans<<endl;
}
signed main()
{
int t;
t=1;
while(t--) work();
return 0;
}