2021-2-22 队内个人赛

2021-2-22 个人赛

A题 CodeForces 1005B

简单思维
两个字符串 从左边删除 问一个删除多少个后两个字符串相同(或者空)
思路就是从后面遍历 找出两个字符串相同的后缀大小 然后用两个字符串总长度去减

#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
 
int main()
{
	char s1[200001], s2[200001];
	int l1, l2, i, j, res;
	scanf("%s%s", s1, s2);
	l1 = strlen(s1);
	l2 = strlen(s2);
	res = l1 + l2;
	for (i = l1 - 1, j = l2 - 1; i >= 0 && j >= 0; i--, j--)
	{
		if (s1[i] == s2[j])
		{
			res -= 2;
		}
		else
		{
			break;
		}
	}
	printf("%d\n", res);
	return 0;
}

B题 CodeForces 1042A

简单思维和贪心

#include<iostream> 
using namespace std;
int main(){
	int a[101];
	int n,m;
	cin>>n>>m;
	int max=0;
	for(int i=0;i<n;i++){
		cin>>a[i]; 
		if(a[i]>a[max])max=i;
	}
	int x;
	int min=0;
	x=a[max]+m;
	while(m--){

		for(int i=0;i<n;i++){
		if(a[i]<a[min])min=i;
	}
	a[min]=a[min]+1;
			
	}
	int max1=0;
	for(int i=0;i<n;i++){
		if(a[i]>a[max1])max1=i;
	}
	int y;
	y=a[max1];
	printf("%d %d\n",y,x);
	
	return 0;
}

C题 CodeForces 1153A

简单思维

#include<iostream> 
using namespace std;
const int N=100010;
int a[N],b[N];
int main(){
   int n,t;
   cin>>n>>t;
   for(int i=1;i<=n;i++){
   	 cin>>a[i]>>b[i];
   }
   int max=1;
   

   for(int i=1;i<=n;i++){
   	if(a[i]<t){
   	 while(a[i]<t){
 	   	a[i]+=b[i];
 	   }	   	
	   }
   }
   for(int i=1;i<=n;i++){
   	if(a[i]>=t){
   		if(a[i]<a[max])
	    max=i;
   	}
   }
   printf("%d\n",max);
}

D题 CodeForces - 1153B

#include<bits/stdc++.h>
#define llINF 9223372036854775807
#define pi 3.141592653589793//23846264338327950254
#define ll long long

using namespace std;
const ll maxn=1e3+7;
const double eps=1e-10;
const ll mod=1e9+7;

//对于每个位置的数字,他只能取所在行规定的最大值maxr和所在列规定的最大值maxc中更小的那个,也就是min(maxr,maxc)
//并且我们取到这个最大值是满足构造条件的
//因此我们直接让每个位置都为这个位置可以存放的最大值就可

int main()
{
   
    ll n,m,h;
    ll field[maxn][maxn];
    ll maxr[maxn],maxc[maxn];
    cin>>n>>m>>h;
    for(ll i=1;i<=m;i++)  cin>>maxc[i];
    for(ll i=1;i<=n;i++)  cin>>maxr[i];
    for(ll i=1;i<=n;i++)
    {
        for(ll j=1;j<=m;j++)
        {
            cin>>field[i][j];
            if(field[i][j]) field[i][j]=min(maxr[i],maxc[j]);
        }
    }
    for(ll i=1;i<=n;i++)
    {
        for(ll j=1;j<=m;j++)
        {
            if(j>1) cout<<' ';
            cout<<field[i][j];
        }
        cout<<endl;
    }
}


E题 POJ 3278

本题两种思路,可用bfs和dp来解决。
算是bfs模板题。
下面为bfs的做法

#include <iostream>
#include <queue>

using namespace std;

const int MAXN = 1e5+10;
queue <int> q;
int step[MAXN];
int vis[MAXN];

int BFS(int n, int k)
{
    int head, np;
    q.push(n);
    step[n] = 0;
    vis[n] = 1;
    while(!q.empty())
    {
        head = q.front();
        q.pop();
        for (int i = 0; i < 3; i++)
        {
            if (i == 0) np = head * 2;
            else if (i == 1) np = head -1;
            else           np = head +1;

            if (np < 0 || np >= MAXN || vis[np]) continue;
            
            q.push(np);
            vis[np] = 1;
            step[np] = step[head] + 1;
            if (np == k)
                return step[np];
        }
    }
    return 0;
}

int main()
{
    int n, k;
    cin >> n >> k;
    if (n >= k)
        cout << n-k << endl;
    else
        cout << BFS(n, k) << endl;

    return 0;
}

考虑四个状态。p点本身,p-1的最优+1,p+1的最优+1,p/2的最优+1(对于偶数的点)
首先,以N为中心,i为从0到K的变量。则从i到N的最普通的方法就是进行平移,所以给初始DP数组赋值为距离。
接下来,对于奇数的,只考虑前3个。由于p+1的最优只有可能是dp[i+1](由于从小到大遍历,此时还没有变化,是最基本的值。)和dp[(i+1)/2]+1,所以将这两个都考虑进来。又由于dp[i+1]=dp[i]+1,所以不再考虑dp[i+1]。同时,也不考虑dp[p/2]+1。
对于偶数,比上述的多考虑一个dp[p/2]+1就完事了。
下面是dp解法

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int N,K;
const int MAX = 100005;
int dp[MAX];

int main(){
	cin>>N>>K;
	for(int i = 0;i <= K;i++){
		dp[i] = abs((int)(N - i));
	}
    for(int i=N+1;i<=K;i++){
        if(i & 1){//奇数
            dp[i]=min(dp[i],dp[(i+1)/2]+2);
            dp[i]=min(dp[i],dp[i-1]+1);
            //由于此时没有更新i+1的东西,所以dp[i+1]-1=dp[i]
        }
        else{
            dp[i]=min(dp[i],dp[i/2]+1);
            dp[i]=min(dp[i],dp[i-1]+1);
            dp[i]=min(dp[i],dp[(i+1)/2]+2);
        }
    }
	cout<<dp[K]<<endl;
	return 0;
}


F题 HDU2063

匈牙利算法模板题
注意每组样例要记得初始化数组。

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 510 , M = 200010;

int h[N],ne[M],e[M],idx;
int g[N];
bool st[N];
int k,n,m;
void add(int a,int b)
{
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void init()
{
    memset(h, -1, sizeof h);
    memset(g,0,sizeof g);
    
    idx = 0;
}
bool find(int x)
{
    for(int i=h[x];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(!st[j])
        {
            st[j]=true;
            if(g[j]==0 || find(g[j]))
            {
                g[j]=x;
                return true;
            }
        }
    }
    return false;
}

int main()
{
    
    while(~scanf("%d",&k) && k){
        
        init();
        scanf("%d%d", &m, &n);
       
        int a, b;
        for(int i=0;i<k;i++)
        {
            scanf("%d%d", &a, &b);
            add(a, b);
        }
        int res = 0;
        for (int i = 1; i <= m; i ++ )
        {
            
            memset(st, false, sizeof st);
            if (find(i)) res ++ ;
            
        }
    
        printf("%d\n", res);
        
    }
    
    return 0;
}

G题 POJ 3641

给出一个数,判断其是否是伪素数。一个数是伪素数满足下面两个条件:
1,p不是素数。 2,满足费马定理a^p==a(mod p)
考查一个判断素数和快速幂的知识点。


#include <iostream>

using namespace std;

typedef long long ll;

bool testPrime(ll x) {
    for (ll i=2; i*i<=x; i++)
        if(x % i == 0)
            return false;
    return true;
} 

//快速幂运算
ll mod_pow(ll x, ll n, ll mod) {
    ll res = 1;
    while(n > 0) {
        if(n & 1)   res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

ll a, p;

int main() {

    

    while(~scanf("%lld%lld", &p, &a) && a && p) {
        if(testPrime(p)) {
            printf("no\n");
        } else {
            if(mod_pow(a, p, p) == a % p)
                printf("yes\n");
            else
                printf("no\n");
        }
    }

  

    return 0;
}

H题 CodeForces 106C

多重背包dp板子题

#include<bits/stdc++.h>
#define llINF 9223372036854775807
#define ll long long
using namespace std;
const ll maxn=1e3+7;
const double eps=1e-10;
const ll mod=1e9+7;

//挺裸的一个多重背包dp


ll w[maxn],v[maxn],num[maxn];
//w[i]代表制作一份第i种糕点需要的面粉数量,v[i]为售出一份第i种糕点获得的收益
//num[i]为第i种糕点从馅料角度看能制作的最大份数,用ai/bi即可得到

int main()
{
   
    ll n,m,c0,d0;
    cin>>n>>m>>c0>>d0;
    ll a,b;
    for(ll i=1;i<=m;i++)
    {
        cin>>a>>b;
        cin>>w[i]>>v[i];
        num[i]=a/b;
    }
    w[m+1]=c0;v[m+1]=d0;num[m+1]=llINF; //增加第m+1种糕点,也就是无馅料的糕点,制作上限为无穷个
    ll dp[13][maxn];    //dp[i][j]代表制作前i种糕点,使用j份面团获得的最大收益
    memset(dp,0,sizeof(dp));
    for(ll i=1;i<=m+1;i++)  //i是当前制作的糕点种类
    {
        for(ll j=0;j<=n;j++)        //j是当前的初始面团份数
        {
            for(ll k=0;j-k*w[i]>=0&&k<=num[i];k++)      //k是当前糕点制作多少份,j-k*w[i]>=0保证了面团够用,k<=num[i]保证了馅料够用
            {
                dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]);    //多重背包的状态转移方程自己去看书或者别人博客吧.
            }
        }
    }
    cout<<dp[m+1][n]<<endl;
}


I题 CodeForces 294C

把每关一次灯看做成一种操作,那么显然要操作的次数等同于灭着的灯的次数。
然后是,把每盏亮着的灯看成是分割线,那么就可以把所有灭着的灯变成一个个的区间(长度可以为0),每个区间的长度为 a [ i ] − a [ i − 1 ] − 1,那么要使得这个区间的灯全部变量,就需要在这个区间上操作 a [ i ] − a [ i − 1 ] − 1 次。
除了最后一次操作,你每次都可以选择关上这个区间左边的灯或关上这个区间右边的灯,那么对于每个区间就有 2 ^(len − 1)
种不同的方法来把这个区间的灯全部打开。
位于两边的区间是特别的,在最左边的区间只能从右边操作,而在最右边的区间只能从左边操作,(如果没有,则视为是长度为0的区间)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1007;
const int mod=1e9+7;
int a[maxn];
ll fac[maxn],c[maxn][maxn];
ll posmod(ll a){
    return (a%mod+mod)%mod;
}
void calfac(){//fac[i]表示2^{i-1}次方,0除外
    fac[0]=fac[1]=1;
    for(int i=2;i<maxn;i++)
        fac[i]=posmod(fac[i-1]*2);
}
void calc(){//递推求组合数
    c[0][0]=1;
    for(int i=1;i<maxn;i++){
        c[i][0]=1;
        for(int j=1;j<=i;j++)
            c[i][j]=posmod(c[i-1][j]+c[i-1][j-1]);
    }
}
int main(){
  
    int x,y;
    calfac();
    calc();
    while(cin>>x>>y){
        for(int i=1;i<=y;i++)
            cin>>a[i];
        sort(a+1,a+y+1);
        int left=x-y;
        ll ans=1;
        for(int i=1;i<=y;i++){//最后一个区间肯定是C_{x}^{x}的情况,就省去了
            ans=posmod(ans*c[left][a[i]-a[i-1]-1]);
            left-=a[i]-a[i-1]-1;
        }
        for(int i=2;i<=y;i++)
            ans=posmod(ans*fac[a[i]-a[i-1]-1]);
        cout<<ans<<endl;
    }
}

J题 CodeForces - 1244D

#include<bits/stdc++.h>
#define llINF 9223372036854775807
#define mp make_pair
#define pb push_back
#define ll long long
using namespace std;
const ll maxn=1e5+7;
const double eps=1e-10;
const ll mod=1e9+7;

//首先需要推一下无法染色的情况,其实就是某一个点存在三条或更多的边的时候,必然会出现染色冲突的情况(自己画图证明)

//再由任意三个相邻的点颜色必不同,可以推出整棵树其实把所有点分成三个部分,每个部分的所有点颜色必然是相同的

//之后我们可以从某一个叶子结点出发dfs,把整个树的点分割成三个部分,再枚举这三个部分对应颜色计算即可

vector<ll>field[maxn];
ll val[3][maxn];
ll cas[maxn];

ll color[6][3]=
{
    1,2,3,
    1,3,2,
    2,1,3,
    2,3,1,
    3,1,2,
    3,2,1
};

void dfs(ll now,ll ope)
{
    if(cas[now]==-1)
    {
        cas[now]=ope;
        ope=(ope+1)%3;
        for(ll i=0;i<field[now].size();i++)
            dfs(field[now][i],ope);
    }
}

int main()
{
 
    ll n;
    cin>>n;
    for(ll i=1;i<=n;i++) cin>>val[0][i];
    for(ll i=1;i<=n;i++) cin>>val[1][i];
    for(ll i=1;i<=n;i++) cin>>val[2][i];
    bool flag=1;
    for(ll i=1;i<n;i++)
    {
        ll u,v;
        cin>>u>>v;
        field[v].pb(u);
        field[u].pb(v);
        if(field[v].size()>2) flag=0;
        if(field[u].size()>2) flag=0;
    }
    if(flag)
    {
        ll ans=llINF;
        ll tar=-1;
        for(ll i=1;i<=n;i++)
        {
            if(field[i].size()==1) tar=i;
            cas[i]=-1;
        }
        dfs(tar,0);
        ll col=-1;
        for(ll i=0;i<6;i++)
        {
            ll temp=0;
            for(ll j=1;j<=n;j++)
                temp+=val[color[i][cas[j]]-1][j];
            if(temp<ans)
            {
                ans=temp;
                col=i;
            }
        }
        cout<<ans<<endl;
        for(ll i=1;i<=n;i++)
        {
            if(i>1) cout<<' ';
            cout<<color[col][cas[i]];
        }
        cout<<endl;
    }
    else cout<<-1<<endl;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值