一点题目

Changing a String

题意:

给定两个字符串a,b,可以对A进行三种操作,删除a中任意位置一个字符,在a中任意位置插入一个任意字符,将a中某个位置的字符更换成任意字符,
现让a,b相等,输出最小操作次数,并且输出每次具体操作。

分析:

dp[i][j]表示将串a的前i个字符变成串b的前j个字符所需要的最小操作次数

串a的第i个和串b的第j个相等,dp[i][j]=dp[i-1][j-1]

如果串a的第i个和串b的第j个不相等:
在串a中插入字符:dp[i][j]=dp[i][j-1]+1//现在a的前i个字符只能满足与b的前j-1个字符相同,所以此时需要插入
在串a中删除字符:dp[i][j]=dp[i-1][j]+1//a的前i-1个字符就满足与b的前j个字符相同,所以此时需要删除
在串a中替换字符:dp[i][j]=dp[i-1][j-1]+1//a的前i-1个字符满足与b的前j-1个字符相同,此时将a中第i个字符替换成b的第j个字符

代码:

#include<iostream>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<cstring>
#include<set>
#include<queue>
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N   = 2E6 + 7;
using namespace std;
ll ksm(ll a,ll b) {ll res=1;a%=mod; while(b){if(b&1)res=res*a%mod;b>>=1;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) {return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
ll inv(ll x) {return ksm(x, mod-2);}
void solve(){
    vector<string>s(2);
    cin>>s[0]>>s[1];
    int n=s[0].size(),m=s[1].size();
    s[0]=' '+s[0];s[1]=' '+s[1];
    vector<vector<int>>dp(n+1,vector<int>(m+1,0));
    for(int i=0;i<=n;i++)dp[i][0]=i;
    for(int j=0;j<=m;j++)dp[0][j]=j;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            dp[i][j]=min(min(dp[i-1][j],dp[i][j-1])+1,dp[i-1][j-1]+(s[0][i]!=s[1][j]));
    cout<<dp[n][m]<<'\n';
    while(n!=0||m!=0){
        if(n&&dp[n][m]==dp[n-1][m]+1)cout<<"DELETE "<<n<<'\n',n--;
        else if(m&&dp[n][m]==dp[n][m-1]+1)cout<<"INSERT "<<n+1<<' '<<s[1][m]<<'\n',m--;
        else if(n&&m){
            if(dp[n][m]==dp[n-1][m-1]+1)
                cout<<"REPLACE "<<n<<' '<<s[1][m]<<'\n';
            n--,m--;
        }
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int t=1;//cin>>t;
    while(t--)
        solve();
}

Pie or die 博弈

题意:

大意,一个n乘m的棋盘,棋盘中每行与列的交汇处可以放棋子,一共k个棋子。先手可移动其中一个棋子,后手在任意两点可放子的点的中间放一个阻碍,使得之后先手不能走这里。如果先手可以移动出一枚棋子,那就可以,否则就不行。

分析:

注意四个角,若棋子处于角上,则无论堵上哪一边,棋子都能从另一边出去。
所以只要他们其中离边界的距离最小是5就好了

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()  
{  
    int n,m,k;
    cin>>n>>m>>k;
    if(k==0)return 0*printf("NO\n");
    else
    {
        int flag=0;
        for(int i=0;i<k;i++)
        {
            int a,b;
            cin>>a>>b;
            if(a<=5 || a>=n-4 || b<=5 || b>=m-4)flag=1;
        }
        printf("%s\n",flag?"YES":"NO");
    }
    return 0;  
}

C.First Digit Law

题意:

给出n个区间[Li,Ri],ai是等概率从第i个区间中选取的一个数字,问a1,...,an中有至少K%的数最高位为1的概率

分析:

统计[l,r]中最高位为1的数字个数x,那么a最高位为1的概率为x/(r-l+1)
以dp[i][j]为前i个数字中有j个数字最高位为1的概率,那么根据第i个数字最高位是否为1得到转移方程
dp[i][j]=pdp[i-1][j-1]+(1-p)dp[i-1][j]对于100i>=kn的i,累加dp[n][i]为答案
(p为概率)

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1005;
int n,K;
ll f[20];
double p[maxn],dp[maxn][maxn];
ll Solve(ll N)
{
    if(!N)return 0;
    int a[100],res=0;
    while(N)a[res++]=N%10,N/=10;
    ll ans=0;
    if(a[res-1]>1)ans+=f[res-1];
    else
    {
        for(int i=0;i<res-1;i++)ans+=f[i]*a[i];
        ans++;
    }
    for(int i=0;i<res-1;i++)ans+=f[i];
    return ans;
}
int main()
{
    f[0]=1;
    for(int i=1;i<=18;i++)f[i]=10ll*f[i-1];
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        ll L,R;
        scanf("%I64d%I64d",&L,&R);
        p[i]=1.0*(Solve(R)-Solve(L-1))/(R-L+1);
    }
    scanf("%d",&K);
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=i;j++)
        {
            if(j==0)dp[i][j]=(1.0-p[i])*dp[i-1][j];
            else dp[i][j]=p[i]*dp[i-1][j-1]+(1.0-p[i])*dp[i-1][j];
        }
    double ans=0;
    for(int i=n;i>=0;i--)
        if(i*100>=K*n)ans+=dp[n][i];
        else break;
    printf("%.12f\n",ans); 
    return 0;
}

Three Base Stations

题意:

给出n个家庭的位置坐标,现建立3个基站,基站以所在位置为圆心,能辐射半径为d的圆的面积,问要让所有家庭都被辐射到,求基站所在位置和半径d

分析:

二分答案,二分d,且找下一个不在当前基站辐射范围内的家庭也要用到二分

#include<iostream>
#include<cstring>
#include<iomanip>
#include<algorithm>
using namespace std;
const int mm=2e5+9;
const double oo=1e39;
const double ex=1e-7;
double locat[mm];
int n,ans[mm];
int blook(int x)///返回比x大的第一个数的下标
{
  int l=0,r=n,mid=(l+r)/2;
  while(l<r)
  {
    if(locat[mid]>x)r=mid;
    else l=mid+1;
    mid=(l+r)/2;
  }
  return mid;
}
bool ok(int x)
{ int z=0;
  for(int i=0;i<3;i++)
  {
    z=blook(locat[z]+x);
    ans[i]=z;
    if(z==n)return 1;
  }
  return 0;
}
 
int main()
{
  while(cin>>n)
  {
    for(int i=0;i<n;i++)
      cin>>locat[i];
    sort(locat,locat+n);
    int r=2e9+9,l=0,mid;
    while(l<r)
    {
      mid=(l+r)/2;
      if(ok(mid))r=mid;
      else l=mid+1;
    }
    ok(l);
    double len=l;len/=2.0;
    double a,b,c;a=locat[ans[0]-1]+locat[0];b=locat[ans[1]-1]+locat[ans[0]];
    c=locat[ans[2]-1]+locat[ans[1]];
    cout.setf(ios::fixed);
    cout<<setprecision(6)<<len<<"\n"<<a/2.0<<" "<<b/2.0<<" "<<c/2.0<<endl;
  }
}

Hercule Poirot Problem

题意:

有n个房间和m扇门,每扇门有且仅有一把钥匙
有k个人度过了两天,在第一天开始的时候所有的门都是关闭的,在第二天结束的时候,所有的门也都是关闭的
在这两天内,每个人可以执行如下操作若干次:
关上一扇门(前提:他有这扇门的钥匙,且这扇门与当前房间相连)
打开一扇门(前提:他有这扇门的钥匙,且这扇门与当前房间相连)
将自己手上的一些钥匙给与他同处于一间房里的其他人
走向一个与当前房间相连、且门是打开的房间
给出第一天开始前和第二天结束后每个人的位置及拥有的钥匙
判断是否可行

分析:

可以发现,如果有解,一定可以把能打开的门都打开,在关回去。
而开和关是互逆的,所以,可以理解为两天都把能打开的门都打开。
如果都打开后,两天结果一样,则有解。
一样就是指打开的门相同,每个房间,人,钥匙所属集合分别相同。
这样就好做了。
暴力枚举能打开的边,用并查集维护每个集合的钥匙,暴力合并。
可以发现,枚举m
次就够了。每次暴力合并复杂度为O(m)
,总时间复杂度为O(m2)

代码:

#include <stdio.h> 
#include <map> 
using namespace std;
#define ull unsigned long long
#define se 13131 
int u[1005],v[1005],fa[1005],n,m,k;
bool zt[1005][1005];
int getv(int x) {
    if (x == fa[x]) return x;
    fa[x] = getv(fa[x]);
    return fa[x];
}
void merge(int x, int y) {
    x = getv(x);
    y = getv(y);
    if (x == y) return;
    if (x < y) {
        int t = x;
        x = y;
        y = t;
    }
    fa[x] = y;
    for (int i = 1; i <= m; i++) {
        if (zt[x][i]) zt[y][i] = true;
    }
}
ull ha(char zf[1005]) {
    ull rt = 0;
    for (int i = 0; zf[i] != 0; i++) rt = rt * se + zf[i];
    return rt;
}
map < ull,int > mp;
char zf[1005];
int wz[1005],rw[1005],yw[1005];
int w2[1005],r2[1005],y2[1005];
bool b1[1005],b2[1005];
int main() {
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= m; i++) scanf("%d%d", &u[i], &v[i]);
    for (int i = 1; i <= k; i++) {
        int a,s;
        scanf("%s%d%d", zf, &a, &s);
        rw[i] = a;
        mp[ha(zf)] = i;
        for (int j = 0; j < s; j++) {
            int b;
            scanf("%d", &b);
            zt[a][b] = 1;
            yw[b] = a;
        }
    }
    for (int a = 1; a <= m; a++) {
        for (int i = 1; i <= m; i++) {
            if (!b1[i] && (zt[getv(u[i])][i] || zt[getv(v[i])][i])) {
                merge(u[i], v[i]);
                b1[i] = true;
            }
        }
    }
    for (int i = 1; i <= n; i++) wz[i] = getv(i);
    for (int i = 1; i <= m; i++) yw[i] = getv(yw[i]);
    for (int i = 1; i <= k; i++) rw[i] = getv(rw[i]);
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
        for (int j = 1; j <= m; j++) zt[i][j] = 0;
    }
    for (int i = 1; i <= k; i++) {
        int a,s;
        scanf("%s%d%d", zf, &a, &s);
        r2[mp[ha(zf)]] = a;
        for (int j = 0; j < s; j++) {
            int b;
            scanf("%d", &b);
            zt[a][b] = 1;
            y2[b] = a;
        }
    }
    for (int a = 1; a <= m; a++) {
        for (int i = 1; i <= m; i++) {
            if (!b2[i] && (zt[getv(u[i])][i] || zt[getv(v[i])][i])) {
                merge(u[i], v[i]);
                b2[i] = true;
            }
        }
    }
    for (int i = 1; i <= n; i++) w2[i] = getv(i);
    for (int i = 1; i <= m; i++) y2[i] = getv(y2[i]);
    for (int i = 1; i <= k; i++) r2[i] = getv(r2[i]);
    bool zd = false;
    for (int i = 1; i <= n; i++) {
        if (wz[i] != w2[i]) zd = true;
    }
    for (int i = 1; i <= n; i++) {
        if (rw[i] != r2[i]) zd = true;
    }
    for (int i = 1; i <= n; i++) {
        if (yw[i] != y2[i]) zd = true;
    }
    for (int i = 1; i <= m; i++) {
        if (b1[i] != b2[i]) zd = true;
    }
    if (zd) printf("NO");
    else printf("YES");
    return 0;
}

Game

题意:

给一串01字符串,可以改动相邻相同颜色的中的两个位置的颜色,最少多少次操作能改成01相间的情况

思路:

直接先考虑结果状态,结果一定为0101010或者10101010,要么是10相间要么是01相间
所以最后直接统计这两种情况中的每个字符不相同的最小数目就可
可以发现
id 1 2 3 4 5 6
val 0 1 0 1 0 1
值加下标都为奇数
id 1 2 3 4 5 6
val 1 0 1 0 1 0
值加下标都为偶数
如果该位s[i]+i为偶数,说明不匹配奇数的那种情况多了一位
为奇数的话,说明不匹配偶数的那种情况多了一位
最后取两者不匹配的最小值即可

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=1e5+10;
#define inf 0x3f3f3f3f
/*
int read(){
    int f=1,x=0;char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
*/
int n,m,k;
string s;
void solve(){
    cin>>n;cin>>s;s=" "+s;
    int a=0,b=0;
    for(int i=1;i<=n;i++){
        if((s[i]+i)%2==0)a++;
        else b++;
    }
    cout<<min(a,b);
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

Happy Farm 5

题意:

在坐标平面内,给出几个点的坐标,
算出将所有点包围起来所需的移动次数,
移动方式:水平或竖直移动单位1,对角线移动根号2,
要求包围圈不能经过点,包围圈沿网格线或经过网格的对角线

分析:

最终的图形一定是一个八边形,也就是长方形把四个角往里缩成斜线。因为走斜线显然比走直线和横线要优。然后就只要找出长方形,再算一下四个角可以缩多少,就可以了。

代码:

#include<bits/stdc++.h>
using namespace std;
 
int n,ans;
int x[100010],y[100010];
int up=-1000000,down=1000000,lft=1000000,rht=-1000000;
int f[4];
 
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
	   scanf("%d%d",&x[i],&y[i]);
	   up=max(up,y[i]+1);down=min(down,y[i]-1);
	   rht=max(rht,x[i]+1);lft=min(lft,x[i]-1);
	}
	f[0]=f[1]=f[2]=f[3]=2000000;
	for(int i=1;i<=n;i++) f[0]=min(f[0],(up-y[i]+x[i]-lft-1));
	for(int i=1;i<=n;i++) f[1]=min(f[1],(up-y[i]+rht-x[i]-1));
	for(int i=1;i<=n;i++) f[2]=min(f[2],(y[i]-down+x[i]-lft-1));
	for(int i=1;i<=n;i++) f[3]=min(f[3],(y[i]-down+rht-x[i]-1));
	ans=rht*2-lft*2+up*2-down*2-f[0]-f[1]-f[2]-f[3];
	printf("%d\n",ans);
	return 0;
}
posted @ 2023-09-17 19:38  WW爆米花  阅读(8)  评论(0编辑  收藏  举报