• HIT第三周 周测补题


    C    HDU 3488

    有N个城市和M条带权的有向的路,要求设计多条闭合的环状线路,使得每个城市均只被其中的一条线路经过一次(线路的初始点除外),并且所有线路经过的路的权值之和最小。

    对于有向圈上的每一个点,它们的出度和入度都一定为1。因此将每个点拆分为两个点,做最大权最大匹配(因为题目要求权值最小,所以把边权变为负数做,最后再把答案乘上-1),得到的结果就是答案了。(我完全没想到这么做。。)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 205
    #define inf 99999999
    using namespace std;
    int vis_g[maxn],vis_b[maxn],ex_g[maxn],ex_b[maxn],slack[maxn];
    int n,mp[maxn][maxn],match[maxn];
    int dfs(int x)
    {
        int i,tmp;
        vis_g[x]=1;
        for (i=1;i<=n;i++)
        {
            if (vis_b[i]) continue;
            tmp=ex_g[x]+ex_b[i]-mp[x][i];
            if (tmp==0)
            {
                vis_b[i]=1;
                if (!match[i] || dfs(match[i]))
                {
                    match[i]=x;
                    return 1;
                }
            }
            else slack[i]=min(slack[i],tmp);
        }
        return 0;
    }
    int km()
    {
        int i,j,re=0,tmp;    
        for (i=1;i<=n;i++)
        {
            ex_g[i]=0;
            ex_b[i]=0;
            match[i]=0;
            for (j=1;j<=n;j++) ex_g[i]=max(ex_g[i],mp[i][j]);    
        }
        for (i=1;i<=n;i++)
        {
            for (j=1;j<=n;j++) slack[j]=inf;
            while (1)
            {
                memset(vis_g,0,sizeof(vis_g));
                memset(vis_b,0,sizeof(vis_b));
                if (dfs(i)) break;
                tmp=inf;
                for (j=1;j<=n;j++)
                    if (!vis_b[j]) tmp=min(tmp,slack[j]);
                for (j=1;j<=n;j++) 
                {
                    if (vis_g[j]) ex_g[j]-=tmp;
                    if (vis_b[j]) ex_b[j]+=tmp;
                    else slack[j]-=tmp;
                }
            }
        }
        for (i=1;i<=n;i++)
            re+=mp[match[i]][i];
        return re;
    }
    int main()
    {
        int i,j,T,m,x,y,z;
        scanf("%d",&T);
        while (T--)
        {
            scanf("%d%d",&n,&m);
            for (i=1;i<=n;i++)
            for (j=1;j<=n;j++) mp[i][j]=-inf;
            while (m--)
            {
                scanf("%d%d%d",&x,&y,&z);
                if (-z<mp[x][y]) continue;
                mp[x][y]=-z;
            }
            printf("%d
    ",-km());
        }
        return 0;
    } 
    View Code

    E    CodeForces 1207G

    AC自动机+DFS序树状数组更新答案。()

    F    CodeForces 696D

    AC自动机+DP+矩阵快速幂。()

    G    CodeForces 1038E

    将两端颜色看作点,中间的权值看作边权,则题中所给的N个block可以看作N条边。

    题目就变成了求给定图中边权之和最大的欧拉通路或欧拉回路(不是严谨的欧拉通路/回路,因为并不一定要经过图中的每一条边)。

    而只有图中度为奇数的点的数量等于2时,图中才会存在欧拉通路;只有图中所有顶点的度均为偶数时,图中才会存在欧拉回路。因此图中的奇度顶点数必须小于等于2。

    由于题目所给的图中最多只有四个顶点(四种颜色),所以奇度顶点数最多为4,这时只要删去一条边就可满足欧拉通路/回路出现的条件。

    于是就枚举每一条边删去的情况,检查图中奇度顶点数是否小于等于2,dfs得到欧拉通路/回路的边权之和,更新答案。

    #include<cstdio>
    #include<cstring>
    #define maxn 5
    #define maxm 205
    using namespace std;
    int num,last[maxn],vis[maxm],used[maxn],deg[maxn],re;
    struct edge
    {
        int to,from,nxt,w;
    }e[maxm];
    void add(int x,int y,int z)
    {
        e[++num].to=y;
        e[num].from=x;
        e[num].w=z;
        e[num].nxt=last[x];
        last[x]=num;
    }
    void dfs(int u)
    {
        used[u]=1;
        for (int i=last[u];i!=-1;i=e[i].nxt)
        {
            if (!vis[i] && !vis[i^1])
            {
                vis[i]=1;
                dfs(e[i].to);
                re+=e[i].w;
            }
        }
    }
    int main()
    {
        int i,j,k,n,x,y,z,cnt,ans;
        while (scanf("%d",&n)!=EOF)
        {
            num=-1;ans=0;
            memset(last,-1,sizeof(last));
            memset(deg,0,sizeof(deg));
            for (i=1;i<=n;i++)
            {
                scanf("%d%d%d",&x,&z,&y);
                add(x,y,z);
                add(y,x,z);
                deg[x]++;
                deg[y]++;
            }
            for (i=0;i<=num+1;i++)//num+1为不删边的情况 
            {
                if (i!=num+1 && e[i].from==e[i].to) continue;
                if (i!=num+1)
                {
                    deg[e[i].from]--;
                    deg[e[i].to]--;
                }
                for (j=1;j<=4;j++)
                {
                    memset(used,0,sizeof(used));
                    memset(vis,0,sizeof(vis));
                    if (i!=num+1) vis[i]=1;
                    re=0;cnt=0;
                    dfs(j);
                    for (k=1;k<=4;k++)
                        if (used[k] && deg[k]%2==1) cnt++;
                    if (cnt<=2 && ans<re) ans=re;
                }
                if (i!=num+1)
                {
                    deg[e[i].from]++;
                    deg[e[i].to]++;
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    I    HDU 2435

    J    HDU 4322

    巧妙建图+最大费用最大流。题解见:https://www.cnblogs.com/wally/archive/2013/08/28/3287980.html

    #include<cstdio>
    #include<cstring>
    #include<queue> 
    #include<algorithm>
    #define maxn 50
    #define maxm 5009
    #define inf 99999999 
    using namespace std;
    int num,lst[maxn];
    int pre[maxn],from[maxn],dis[maxn],vis[maxn],flow[maxn];
    int n,m,k,sum,s,t,like[maxn][maxn],b[maxn];
    struct in
    {
        int to,nxt,re,w;
    }e[maxm];
    queue<int>q;
    void add(int x,int y,int z,int w)
    {
        e[++num].to=y;e[num].nxt=lst[x];lst[x]=num;e[num].re=z;e[num].w=w;
        e[++num].to=x;e[num].nxt=lst[y];lst[y]=num;e[num].re=0;e[num].w=-w;
    }
    int spfa()
    {
        int i,u,v;
        while (!q.empty()) q.pop();
        for (i=0;i<=t;++i) 
        {
            pre[i]=-1;//用于记录上一条边 
            from[i]=-1;//用于记录上一个点 
            dis[i]=-inf;
            vis[i]=0;
        }
        q.push(s);
        dis[s]=0;
        flow[s]=inf;
        vis[s]=1;
        while (!q.empty())
        {
            u=q.front();
            q.pop();
            vis[u]=0;
            for (i=lst[u];i!=-1;i=e[i].nxt)
            {
                v=e[i].to;
                if (!e[i].re) continue;
                if (dis[v]<dis[u]+e[i].w)
                {
                    dis[v]=dis[u]+e[i].w;
                    flow[v]=min(e[i].re,flow[u]);
                    pre[v]=i;
                    from[v]=u;
                    if (!vis[v]) 
                    {
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        return pre[t]!=-1;
    }
    int mfmv()
    {
        int mincost=0,maxflow=0,v;
        while (spfa())
        {
            //printf("
    mincost %d and maxflow %d 
    ",mincost,maxflow);
            mincost+=flow[t]*dis[t];
            maxflow+=flow[t];
            v=t;
            while (v!=s)
            {
                e[pre[v]].re-=flow[t];
                e[pre[v]^1].re+=flow[t];
                v=from[v];
            }
        }
        //printf("
    mincost %d and maxflow %d 
    ",mincost,maxflow);
        return n-maxflow>=sum-mincost;
    }
    int main()
    {
        int ti,T,i,j;
        scanf("%d",&T);
        for (ti=1;ti<=T;ti++)
        {
            scanf("%d%d%d",&n,&m,&k);
            num=-1;sum=0;
            s=0;t=n+m+1;
            memset(lst,-1,sizeof(lst));
            for (i=1;i<=m;i++) 
            {
                scanf("%d",&b[i]);
                sum+=b[i]; 
            }
            for (i=1;i<=n;i++) add(s,i,1,0);
            for (i=1;i<=m;i++)
            {
                for (j=1;j<=n;j++) 
                {
                    scanf("%d",&like[i][j]);
                    if (like[i][j]==1) add(j,i+n,1,0);
                }
                add(i+n,t,b[i]/k,k);
                if (b[i]%k>1) add(i+n,t,1,b[i]%k);
            }
            printf("Case #%d: ",ti);
            if (mfmv()) printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    } 
    View Code
  • 相关阅读:
    Java的学习笔记(二)
    Java的学习笔记
    简单界面生成
    登录界面
    播放器
    java计划
    求和
    修改后的抽奖系统
    第十周作业
    JAVA第五次作业
  • 原文地址:https://www.cnblogs.com/lsykk/p/13511180.html
Copyright © 2020-2023  润新知