6232. 最小移动总距离 - 力扣 dp,N - Nunchucks Shop 组合数求不回文的排列,D - Yet Another Problem map记录前缀和的位置

142 篇文章 1 订阅
92 篇文章 0 订阅

N - Nunchucks Shop 组合数求不回文的排列

可以发现对于每个i<=n,j<=n,只要i+j==k我们就可以加上i和j的贡献,所以关键的地方就是算贡献,这其实看起来像组合数,但是如果两个排列或者说方案正着和反着是一样也就是回文的话,那么只保留一种就可以,这个也是有公式的

如果n为偶数,i为奇数,那么就是C(n,i)/2,否则就是(C(n,i)+C(n/2,i/2))/2;

知道这个公式,这个题就结束了

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
//const int mod=1e9+7;
const int inf=1e18;
const int N = 1e7+100;
int a[55][55];
int c[55][55];
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    for(int i=0;i<=50;i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1];
    }
    for(int i=1;i<=50;i++)
    for(int j=0;j<=i;j++)
    {
        if(i%2==0&&(j&1)) a[i][j]=c[i][j]/2;
        else a[i][j]=(c[i][j]+c[i/2][j/2])/2;
    }
    int ans=0;
    int n,k;
    cin>>n>>k;
    for(int i=0;i<=k&&i<=n;i++)
    for(int j=i;j<=k&&j<=n;j++)
    {
        if(i+j==k)
        {
            ans+=a[n][i]+a[n][j];
        }
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}

D - Yet Another Problem map记录前缀和的位置

如果[L,R]的异或和不为0那么一定是无解的,然后如果[l,r]区间是全0的话就是0步,xr是异或前缀和,pre[i]表示离i最近的j且j<i,满足xr[i]^xr[j-1]=0,如果pre[r]>=l,那么就可以两步完成,因为[l,r]异或和是0,如果[l,r]中有一段区间[x,r]异或和为0,那么剩下的异或和也一定是0;注意一个特判:如果a[l]=0||a[r]=0,那么只需要一步就够了

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=1e9+7;
const int inf=1e18;
const int N = 2e5+100;
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int getinv(int a){return qpow(a,mod-2LL);}
int n,q,a[N],nxt[N],xr[N],sum[N],pre[N];
map<int,int>mp[2];
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>q;
    mp[0][0]=0;sum[0]=xr[0]=0;
    for(int i=1;i<=n;i++) pre[i]=0,nxt[i]=n+1;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
        xr[i]=xr[i-1]^a[i];
        int bit=i&1;
        if(mp[!bit].count(xr[i]))
        {
            
            int laspos=mp[!bit][xr[i]];
            //cout<<"i="<<i<<" laspos="<<laspos<<" !bit"<<!bit<<" xr[i]="<<xr[i]<<endl;
            pre[i]=laspos+1;
            //nxt[laspos+1]=min(nxt[laspos+1],i);
        }
        mp[bit][xr[i]]=i;
    }
    while(q--)
    {
        int l,r;
        cin>>l>>r;
        if((xr[r]^xr[l-1])!=0)
        {
            cout<<"-1\n";
            continue;
        }
        if(sum[r]-sum[l-1]==0) cout<<"0\n";
        else
        {
            if((r-l+1)&1) cout<<"1\n";
            else if(a[l]==0||a[r]==0) cout<<"1\n";
            else
            {
               // cout<<nxt[l]<<" "<<l<<" "<<r<<endl;
                if(pre[r]>=l) cout<<"2\n";
                else cout<<"-1\n";
            }
        }
    }
    system("pause");
    return 0;
}

6232. 最小移动总距离 - 力扣(LeetCode) dp

按位置对工厂和机器人排序,然后可以发现对于两个机器人一个位于x,一个位于y且y>x,那么位于y所选的工厂的位置要大于等于x所选的工厂的位置,这样才是最优的,这样这个题就相对来说简单一点了,先预处理出一个d数组,d[i][j]表示前i个机器人到第j个工厂所用的距离之和,f[i][j]表示前i个机器人到前j个工厂所需要的最小的距离之和,这样转移也是可以想一下的,

f[i][j]=min(f[i][j],f[k][j-1]+d[i][j]-d[k][j])

也就是枚举人数和每个工厂,k是前j-1个工厂所治疗的人数,这样第j个工厂就是进了i-k个人,然后看看第j个工厂进多少人才是最优的,这样是可以把所有情况都枚举到的

动态规划 - 最小移动总距离 - 力扣(LeetCode)

#define ll long long
struct node
{
    int pos,lim;
    bool operator<(const node &a)const
    {
        return pos<a.pos;
    }
}fac[205];
class Solution {
public:
    long long minimumTotalDistance(vector<int>& robot, vector<vector<int>>& factory) {
        int ro[205],n=robot.size(),m=factory.size();
        sort(robot.begin(),robot.end());
        sort(factory.begin(),factory.end());
        for(int i=0;i<robot.size();i++) ro[i+1]=robot[i];
        for(int i=0;i<factory.size();i++) fac[i+1].pos=factory[i][0],fac[i+1].lim=factory[i][1];
        sort(fac+1,fac+m+1);
        ll f[205][205],d[205][205];
        for(int j=1;j<=m;j++)
        {
            d[0][j]=0;
            for(int i=1;i<=n;i++)
            {
                d[i][j]=d[i-1][j]+abs(ro[i]-fac[j].pos);
            }
        }
        for(int i=0;i<=n;i++)
        for(int j=0;j<=m;j++) f[i][j]=1e18;
        for(int i=0;i<=m;i++) f[0][i]=0;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            int lim=fac[j].lim;
            for(int k=max(0,i-lim);k<=i;k++)
            {
                f[i][j]=min(f[i][j],f[k][j-1]+d[i][j]-d[k][j]);
            }
        }
        return f[n][m];
    }
};

1475D - Cleaning the Phone 瞎搞或者二分

用了一种比较勉强但是比较合理的思路乱搞过去了,,,

一开始全用类型1,一直加到>=m,如果1全用完 还不行的话就用2再加,用2加到>=m之后就要判断是否可以减去一些1,之后对剩下的2都这样判断,但是其实排完序之后如果当前的这个类型2不行的话以后的就一定不行了,因为是从大到小排序的

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-8;
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}
int getinv(int a){return qpow(a,mod-2LL);}
int t,n,m,a[N],b[N],f[2][N];
int cnt[2]={0,0};
struct node
{
    int id,val;
    bool operator<(const node &a)const
    {
        return a.val<val;
    }
};
bool cmp(int a,int b)
{
    return a>b;
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        cnt[0]=cnt[1]=0;
        priority_queue<node>q;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++)
        {
            cin>>b[i];
            f[b[i]&1][++cnt[b[i]&1]]=a[i];
        }
        sort(f[0]+1,f[0]+cnt[0]+1,cmp);
        sort(f[1]+1,f[1]+cnt[1]+1,cmp);
        int sum=0,res=0;
        for(int i=1;i<=cnt[1];i++)
        {
            sum+=f[1][i];res++;
            //cout<<"sum="<<sum<<" res="<<res<<" f[1][i]="<<f[1][i]<<" "<<i<<endl;
            q.push(node{1,f[1][i]});
            if(sum>=m) break;
        }
        int ma=1;
        if(sum<m)
        {
            for(int i=1;i<=cnt[0];i++)
            {
                sum+=f[0][i];res+=2;
                q.push(node{2,f[0][i]});
                if(sum>=m)
                {
                    ma=i+1;
                    while(!q.empty()&&sum-q.top().val>=m)
                    {
                        sum-=q.top().val;res-=q.top().id;q.pop();
                    }
                    break;
                }
            }
        }
        if(sum<m)
        {
            cout<<"-1\n";continue;
        }
        //cout<<sum<<" "<<res<<" "<<ma<<endl;
        for(int i=ma;i<=cnt[0];i++)
        {
            node a=q.top();q.pop();
            if(q.empty())
            {
                q.push(a);break;
            }
            node b=q.top();q.pop();
            if(a.val+b.val<f[0][i])
            {
                sum+=f[0][i]-a.val-b.val;
                q.push(node{2,f[0][i]});
                while(!q.empty()&&sum-q.top().val>=m)
                {
                    sum-=q.top().val;res-=q.top().id;q.pop();
                }
            }
            else
            {
                break;
            }
        }
        cout<<res<<endl;
        
    }
    system("pause");
    return 0;
}

其实比较正规的解法是二分,对类型1和2从大到小排完序之后枚举使用了多少个1,然后去二分2的使用个数

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-8;
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}
int getinv(int a){return qpow(a,mod-2LL);}
int t,n,m,a[N],b[N],f[2][N],sum[N];
int cnt[2]={0,0};
struct node
{
    int id,val;
    bool operator<(const node &a)const
    {
        return a.val<val;
    }
};
bool cmp(int a,int b)
{
    return a>b;
}

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        cnt[0]=cnt[1]=0;
        f[0][0]=f[1][0]=0;
        int su=0;
        for(int i=1;i<=n;i++) cin>>a[i],su+=a[i];
        for(int i=1;i<=n;i++)
        {
            cin>>b[i];
            f[b[i]&1][++cnt[b[i]&1]]=a[i];
        }
        if(su<m)
        {
            cout<<"-1\n";continue;
        }
        sort(f[0]+1,f[0]+cnt[0]+1,cmp);
        sort(f[1]+1,f[1]+cnt[1]+1,cmp);
        sum[0]=0;
        for(int i=1;i<=cnt[0];i++) sum[i]=sum[i-1]+f[0][i];
        su=0;
        int ans=1e18;
        for(int i=0;i<=cnt[1];i++)
        {
            su+=f[1][i];
            int x=m-su;
            if(x<=0)
            {
                ans=min(ans,i);continue;
            }
            int id=lower_bound(sum+1,sum+cnt[0]+1,x)-sum;
            if(id<=cnt[0]) ans=min(ans,id*2LL+i);
        }
        cout<<ans<<endl;
    }
    system("pause");
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

killer_queen4804

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值