A:
贪心分配黄牌即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)] //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int a,b,k1,k2,n,nn;
cin>>a>>b>>k1>>k2>>n;
nn=n;
if(k1>k2)swap(k1,k2),swap(a,b);
int ni=a*(k1-1)+b*(k2-1);
int w=n/k1,nm=0;
if(w>=a){
n-=a*k1;
nm+=a;
nm+=n/k2;
}
else nm+=n/k1;
cout<<max(nn-ni,0)<<" "<<nm<<endl;
return 0;
}
B:
我是DP写的,很好写
这题也可以思维去做,比较难想。。就是算出负区间的个数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)] //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 2e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
ll f[M][2];//0负数 1正数
int a[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
if(a[1]>0)f[1][1]=1;
else f[1][0]=1;
for(int i=2;i<=n;i++)
{
if(a[i]>0)
f[i][0]=f[i-1][0],f[i][1]=f[i-1][1]+1;
else
f[i][0]=f[i-1][1]+1,f[i][1]=f[i-1][0];
}
ll ans1=0,ans2=0;
for(int i=1;i<=n;i++)ans1+=f[i][0],ans2+=f[i][1];
cout<<ans1<<" "<<ans2<<endl;
return 0;
}
C:
只有a、b个数和为偶数,且s、t不同字符数也要是偶数个
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)] //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 2e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
char s[M],t[M];
int p1[M],p2[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
cin>>s>>t;
int na=0,nb=0,s1=0,s2=0;
for(int i=0;i<n;i++){
if(s[i]==t[i])continue;
// if(s[i]=='a')na++;else nb++;
// if(t[i]=='a')na++;else nb+;
if(s[i]=='a')p1[++s1]=i+1;
else p2[++s2]=i+1;
}
// cout<<s1<<" "<<s2<<endl;
if((s1+s2)&1){
cout<<-1<<endl;
return 0;
}
if(s1%2==0)
{
cout<<(s1+s2)/2<<endl;
for(int i=1;i<=s1;i+=2)cout<<p1[i]<<" "<<p1[i+1]<<endl;
for(int i=1;i<=s2;i+=2)cout<<p2[i]<<" "<<p2[i+1]<<endl;
return 0;
}
{
cout<<s1/2+s2/2+2<<endl;
for(int i=2;i<=s1;i+=2)cout<<p1[i]<<" "<<p1[i-1]<<endl;
for(int i=2;i<=s2;i+=2)cout<<p2[i]<<" "<<p2[i-1]<<endl;
cout<<p1[s1]<<" "<<p1[s1]<<endl;
cout<<p1[s1]<<" "<<p2[s2]<<endl;
}
return 0;
}
D:
这种博弈一般要么SG要么找规律
这题我们发现,先手肯定是想让数的差距尽可能大
判断一下让大的数变大,和让小的数变大能否大于另一个数(即后手无论怎么补救都补救不回来)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)] //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 2e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
char s[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
cin>>s;
int nl=0,nr=0,al=0,ar=0;
for(int i=0;i<n;i++)
if(i<n/2){
if(s[i]=='?')nl++;
else al+=s[i]-'0';
}
else{
if(s[i]=='?')nr++;
else ar+=s[i]-'0';
}
//先手选择让左边大或者右边大
int wl=nl/2*9+al+(nl&1)*9;
int wr=nr/2*9+ar+(nr&1)*9;
//cout<<wl<<" "<<wr<<endl;
if(wl!=wr)//先手想让左边大
{
cout<<"Monocarp"<<endl;
return 0;
}
cout<<"Bicarp"<<endl;
return 0;
}
E:
状压DP
我们发现,最终数如果顺序固定,最优的方案一定是先让左边的数排好,再排次左边的,,然后排完所有的。
一般问题我们可以从边界来考虑,这一题也是。假如最终数的顺序固定,最左边的数先排好肯定是最优,不会变差(因为无论你顺序是什么,总要把最后最左边的数字移到最左边 。比如2 3 3 4 3,我们想让最后的顺序是3 4 2 ,那么无论你怎么移动,先把3移到最左边总是没错的,且是最优的:3 3 3 2 4 ,然后再考虑其他的数)。后面的数又可以归类到前面的问题,同样先排好最左边的数。依次类推。
明显:我们可以枚举已经排序的数的状态。枚举最后一次排序的数,进行递推即可
因为交换两个数时不会改变其他数的相对位置。
所以我们可以记录状态,递推一下即可。
f[i] 表示排好当前的数,所需的最小代价。
排好i数的代价一定是:所有i数前面的其他数的个数(不包括已经排好顺序的数)
预处理出代价就可以状压DP了
我们的策略始终是把确定的数,从左往右排,所以递推时,枚举最后排的数,加上代价递推即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int seed=131;
const int M = 4e5+7;
int ct[21];
int a[M];
ll cost[21][21];
const int N =(1<<20)+1;
ll f[N];//状态已经排好的数的状态,的最小交换次数,从左往右排
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,ma=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
for(int j=1;j<=20;j++)cost[a[i]][j]+=ct[j];//a[i]前面 的a[j]的个数,类比逆序对
ct[a[i]]++;
ma=max(a[i],ma);
}
memset(f,60,sizeof(f));
f[0]=0;
for(int i=0;i<=(1<<ma)-1;i++)//枚举状态 从左往右排到当前的状态
{
for(int j=1;j<=ma;j++)//枚举最后一个排好的数字
{
if(!(i>>(j-1)))continue; //当前状态不存在这个数字
ll tp=0;
for(int k=1;k<=ma;k++)
{
if(i&(1<<(k-1)))continue;//已经排好的数字我们就不计算他的贡献了(因为他们已经在最左边了)
tp+=cost[j][k];
}
f[i]=min(f[i],f[i^(1<<(j-1))]+tp);
}
}
cout<<f[(1<<ma)-1]<<endl;
return 0;
}