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
原文地址:https://www.cnblogs.com/lsykk/p/13511180.html