“浪潮杯”第九届山东省ACM大学生程序设计竞赛

Anagram

一道签到题。
这里试了下用二分图最大权匹配去解。
对于串A的第i个单词,向串B的全部单词连边,权值为题干“转变”的权值的相反数。对这个二分图走一遍KM算法求出最大权匹配,然后取相反数即为所求。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=55;
string s,t;
int a[maxn],b[maxn];
bool vis[maxn];
int from[maxn],w[maxn][maxn];
int lx[maxn],ly[maxn],visx[maxn],visy[maxn],slack[maxn];
int nx,ny;
///定义slack[y]=min{lx[x]+ly[y]-w[x][y]},x已访问,y未访问

bool Find(int u)
{
    visx[u]=1;
    for(int v=0;v<ny;v++)if(!visy[v])
    {
        int tmp=lx[u]+ly[v]-w[u][v];
        if(tmp==0)
        {
            visy[v]=1;
            if(from[v]==-1 || Find(from[v]))
            {
                from[v]=u;
                return true;
            }
        }
        else slack[v]=min(slack[v],tmp);
    }
    return false;
}
int KM()
{
    for(int i=0;i<nx;i++)
    {
        ly[i]=0;
        lx[i]=-INF;
        for(int j=0;j<ny;j++)
            lx[i]=max(lx[i],w[i][j]);
    }
    memset(from,-1,sizeof(from));
    for(int u=0;u<nx;u++)
    {
        for(int i=0;i<ny;i++) slack[i]=INF;
        while(true)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(Find(u)) break;
            int d=INF;
            for(int i=0;i<ny;i++)if(!visy[i])
                d=min(d,slack[i]);
            for(int i=0;i<nx;i++)if(visx[i])
                lx[i]-=d;
            for(int i=0;i<ny;i++)
            {
                if(visy[i]) ly[i]+=d;
                else slack[i]-=d;
            }
        }
    }
    int ans=0;
    for(int i=0;i<ny;i++)
        if(from[i]!=-1) ans+=w[from[i]][i];
    return ans;
}

int main()
{
    while(cin>>s>>t)
    {
        int n=s.size();
        nx=ny=n;
        memset(vis,false,sizeof(vis));
        for(int i=0;i<n;i++)
        {
            a[i]=s[i]-'A';
            b[i]=t[i]-'A';
        }
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
        {
            if(a[i]<=b[j])
                w[i][j]=b[j]-a[i];
            else
                w[i][j]=b[j]-a[i]+26;
            w[i][j]=-w[i][j];
        }
        cout<<-KM()<<endl;
    }
    return 0;
}

Bullet

题意:
给定一个n*n矩阵,矩阵内值非零表示该处有怪物且怪物的经验值为矩阵内值。每一行每一列至多消灭一个怪物,现要求消灭最大的怪物,并在这个前提下求出能获得的最大经验值。你所获得的经验值为你所消灭的所有怪物的经验值中最小的那个。

分析:
二分图最大匹配+二分。
对于所有矩阵内值非零a[i][j],从X部的i点向Y部的j点连一条边。对此二分图求出最大匹配ans。那么ans为最多能消灭的怪物数量。然后二分枚举经验值k,对于所有矩阵内值a[i][j]>=k的,按照上面一样重新建图并求出最大匹配sum。如果sum等于ans,那么经验值k是合法的。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=550;
int n;
int from[maxn],w[maxn][maxn],ans,vis[maxn];
vector<int> g[maxn];
void init()
{
    for(int i=0;i<n;i++) g[i].clear();
}
bool Find(int u)
{
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(!vis[v])
        {
            vis[v]=1;
            if(from[v]==-1 || Find(from[v]))
            {
                from[v]=u;
                return true;
            }
        }
    }
    return false;
}
int match()
{
    int ret=0;
    memset(from,-1,sizeof(from));
    for(int i=0;i<n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(Find(i)) ret++;
    }
    return ret;
}
bool check(int k)
{
    init();
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        if(w[i][j]>=k)
            g[i].push_back(j);
    return match()==ans;
}
int main()
{
    while(~scanf("%d",&n))
    {
        init();
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
            scanf("%d",&w[i][j]);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
        {
            if(w[i][j])
            {
                g[i].push_back(j);
            }
        }
        ans=match();
        int L=0,R=1e9;
        while(L<R)
        {
            int mid=(L+R+1)>>1;
            if(check(mid))
                L=mid;
            else
                R=mid-1;
        }
        printf("%d\n",L);
    }
    return 0;
}

Cities

签到题。

#include <iostream>
#include <cstdio>

using namespace std;
typedef long long ll;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        ll a, minx = 99999999, sum = 0;
        for(int i = 0; i < n; i++)
        {
            scanf("%lld",&a);
            sum += a;
            if(a < minx)
                minx = a;
        }
        printf("%lld\n",sum+(n-2)*minx);
    }
    return 0;
}

Four-tuples

分析:
容斥原理。
设全集U表示满足l[i]<=x[i]<=r[i]的四元组。现还要满足四个条件,x1!=x2,x2!=x3,x3!=x4,x4!=x1.将这四个要求的交集转化成另四个集合的并集问题。
集合A:x1=x2,
集合B:x2=x3,
集合C:x3=x4,
集合D:x4=x1.
这样,难以直接求出的交集问题就转化成了可以利用容斥原理直接求出的简单并集问题了。

代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

typedef long long ll;
const ll mod=1e9+7;
ll cnt[5],l[5],r[5];
ll ans,tmp,sum;
int main()
{
    int T;
   scanf("%d",&T);
    while(T--)
    {
        ans=1;
        for(int i=1;i<=4;i++)
        {
            //cin>>l[i]>>r[i];
            scanf("%lld%lld",&l[i],&r[i]);
            cnt[i]=r[i]-l[i]+1;
            ans=ans*cnt[i]%mod;
        }
        //A
        tmp=cnt[3]*cnt[4]%mod;
        ll le=-INF,ri=INF;
        le=max(le,max(l[1],l[2]));
        ri=min(ri,min(r[1],r[2]));
        ll A=le<=ri?ri-le+1:0;
        ans=(ans-A*tmp)%mod;
        //B
        tmp=cnt[1]*cnt[4]%mod;
        le=-INF,ri=INF;
        le=max(le,max(l[2],(l[3])));
        ri=min(ri,min(r[2],(r[3])));
        ll B=le<=ri?ri-le+1:0;
        ans=(ans-B*tmp)%mod;
        //C
        tmp=cnt[1]*cnt[2]%mod;
        le=-INF,ri=INF;
        le=max(le,max(l[3],(l[4])));
        ri=min(ri,min(r[3],(r[4])));
        ll C=le<=ri?ri-le+1:0;
        ans=(ans-C*tmp)%mod;
        //D
        tmp=cnt[2]*cnt[3]%mod;
        le=-INF,ri=INF;
        le=max(le,max(l[1],(l[4])));
        ri=min(ri,min(r[1],(r[4])));
        ll D=le<=ri?ri-le+1:0;
        ans=(ans-D*tmp)%mod;
        //AB
        le=-INF,ri=INF;
        le=max(le,max(l[1],max(l[2],(l[3]))));
        ri=min(ri,min(r[1],min(r[2],(r[3]))));
        ll AB=le<=ri?ri-le+1:0;
        tmp=cnt[4]*AB%mod;
        ans=(ans+tmp)%mod;
        //BC
        le=-INF,ri=INF;
        le=max(le,max(l[4],max(l[2],(l[3]))));
        ri=min(ri,min(r[4],min(r[2],(r[3]))));
        tmp=cnt[1];
        ll BC=le<=ri?ri-le+1:0;
        tmp=tmp*BC%mod;
        ans=(ans+tmp)%mod;
        //CD
        le=-INF,ri=INF;
        le=max(le,max(l[1],max(l[4],(l[3]))));
        ri=min(ri,min(r[1],min(r[4],(r[3]))));
        tmp=cnt[2];
        ll CD=le<=ri?ri-le+1:0;
        tmp=tmp*CD%mod;
        ans=(ans+tmp)%mod;
        //AC
        tmp=A*C;
        ans=(ans+tmp)%mod;
        //AD
        le=-INF,ri=INF;
        le=max(le,max(l[1],max(l[4],(l[2]))));
        ri=min(ri,min(r[1],min(r[4],(r[2]))));
        tmp=cnt[3];
        ll AD=le<=ri?ri-le+1:0;
        tmp=tmp*AD%mod;
        ans=(ans+tmp)%mod;
        //BD
        tmp=B*D;
        ans=(ans+tmp)%mod;
        //ABC
        le=-INF,ri=INF;
        le=max(le,max(l[1],max(l[4],max(l[2],(l[3])))));
        ri=min(ri,min(r[1],min(r[4],min(r[2],(r[3])))));
        tmp=1;
        ll ABC=le<=ri?ri-le+1:0;
        tmp=tmp*ABC%mod;
        ans=(ans-tmp)%mod;
        //ABD
        tmp=ABC;
        ans=(ans-tmp)%mod;
        //BCD
        tmp=ABC;
        ans=(ans-tmp)%mod;
        //ACD
        tmp=ABC;
        ans=(ans-tmp)%mod;
        //ABCD
        tmp=ABC;
        ans=(ans+tmp)%mod;
        while(ans < 0)
            ans += mod;
        //cout<<ans<<endl;
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37685156/article/details/80382528