【GCJ2008E】日程表 最小割

Google Code Jam 2008 E 日程表

【题目描述】

热情的选手Sphinny正在看新一年的日程表,并发现已经安排了很多编 程竞赛。她将这一年的每一天都用以下三种方式之一在日程表上打标记。

1.白色:这一天她将不参加竞赛。或许这一天没有预定的竞赛,或许 这一天有更重要的事情要做(生活中肯定还有其他美好的事情)。

2.蓝色:这一天她将参加一场竞赛。

3.问号:这一天有预定好的竞赛,但她还没有决定好是否参加。

为了简化问题,我们假设没有资格的概念:你不必参加一场比赛去取得另 一场比赛的参赛资格。

Sphinny生活的世界与我们的世界有所不同,那个世界里一年有n个月, 每个月恰有m天。

下面的图片是一张5个月,每个月有8天的日程表。上面有15天标记为 蓝色,5天标记了问号。

 

看她美丽的日程表。Sphinny认为对于每一天,有四天与它相邻(可能有 的不存在):同一个月的前一天,同一个月的后一天,前一个月的同一天,后 一个月的同一天。

Sphinny想最大化所有竞赛的喜悦值之和。一场竞赛的喜悦值的计算方式 是:

1.初始为4。

2.每有与那一天相邻的一天要参赛,喜悦值减1。(你可以认为Sphinny喜欢竞赛,但连续参赛让她感觉很累。并且出于审美的原因,在相邻 两个月的同一天参赛也不是很好。)

现在,Sphinny想计划这一年,并决定把每一个问号标记都改为白色标记 或蓝色标记。她的目标很简单,就是最大化总喜悦值。

下图是上面例子的一种解决方案。通过把2个问号标记变为蓝色标记,剩 下的3个问号标记变为白色标记,她可以得到喜悦值42。

【输入格式】

第一行一个整数T表示测试数据组数。

对于每一组数据:第一行两个整数n,m表示有n个月,每个月有m天。 之后n行每行一个长度为m的字符串,第i行第j个字符表示第i个月的第j 天的状态,“#”表示这一天被标记为蓝色,“.”表示这一天被标记为白色,"?”表示这一天被标记为问号。

【输出格式】

对于每一组数据,输出: Case #X: Y

X是第几组测试数据,Y是最大总喜悦值。

【输入样例】

2

3 3

.?.

.?.

.#.

5 8

.#...##.

.##..?..

.###.#

.# ??#.

.?.. ##

#?#...

【输出样例】

Case #1: 8

Case #2: 42

【数据范围】

对于30%的数据,1≤n,m≤5

对于60%的数据,1≤n,m≤15

对于100%的数据,1≤n,m≤50,1≤T≤100

Solution

评测链接     评测数据

30%乱搞暴力  60%可以状压DP 100%最小割

要求总收益最大,可以转化为  损失最小  ,然后答案就是可以获得的最大总收益-最小损失可以利用最小割来求解

考虑对方格进行黑白染色

“.”点对答案没有影响,所以不用考虑,考虑“#”“?”的相互影响即可

那么我们先预处理出$tot=\sum val[i] + \sum sc[i] $ 其中 $val[i]$表示“?”点的预期得分(4-2*四联通的“#”数),$sc[i]$表示已经确定的“#”的确定得分

那么考虑最小割建模

S连白色“?”点,容量为 $val[i]$ 

黑色“?”点连T,容量为 $val[i]$

相邻的黑白点,由白“?”点向黑“?”点,容量为$2$

考虑一个割,如果割的是与S相连的边,表示这个白点不变蓝,如果割与T相连,表示这个黑点不变蓝,如果割中间边,表示都变蓝

这里有个值得注意的地方,对于一个“?”点,如果他的四联通中有$>=2$个“#”点,那么这个点我们可以忽略

最后答案为$Ans=tot-mincut$

faebdc学长的标解:

初始化ans为所有问号标记都为白色标记时的喜悦值,再将ans加上问号标记个数的4倍。那么:
1. 问号标记变为白色标记,ans要减4。
2.问号标记变为蓝色标记,若它周围有tot个一开始就是蓝色的标记,ans要减去2*tot。
3. 两个相邻的问号标记?_1和?_2都变为蓝色标记,ans要减去2。
建图时,让每一个问号标记对应着图中的一个点。考虑前两种情况,可以让每个点与源点、汇点各连一条边,流量为4和2*tot。考虑第三种情况,?_1和?_2都变为蓝色标记时,ans要减去2,可以使用下图这样的建图方式。
这样,只有相邻两个问号标记都变为蓝色标记时,连接这两个问号标记的边才会被割掉,使ans减小2。
将网格染色,相邻的格子颜色不同,就会形成二分图。一种颜色的点用?_1的连接方式,另一种颜色的点用?_2的连接方式。
ans减去求出的最大流即为答案。
标准建图

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
void Freopen() {freopen("cal.in","r",stdin); freopen("cal.out","w",stdout);}
void Fclose() {fclose(stdin); fclose(stdout);}
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define MAXM 1000100
#define MAXN 100100
int N,M,cas,Cas;
char G[100][100];
struct EdgeNode{int next,to,cap;}edge[MAXM];
int head[MAXN],cnt=1;
void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].cap=w;}
void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,0);}
int dis[MAXN],que[MAXN<<1],cur[MAXN],S,T;
bool bfs()
{
    for (int i=S; i<=T; i++) dis[i]=-1;
    que[0]=S; dis[S]=0; int he=0,ta=1;
    while (he<ta)
        {
            int now=que[he++];
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i].cap && dis[edge[i].to]==-1)
                    dis[edge[i].to]=dis[now]+1,que[ta++]=edge[i].to;
        }
    return dis[T]!=-1;
}
int dfs(int loc,int low)
{
    if (loc==T) return low;
    int w,used=0;
    for (int i=cur[loc]; i; i=edge[i].next)
        if (edge[i].cap && dis[edge[i].to]==dis[loc]+1)
            {
                w=dfs(edge[i].to,min(low-used,edge[i].cap));
                edge[i].cap-=w; edge[i^1].cap+=w;
                used+=w; if (edge[i].cap) cur[loc]=i;
                if (used==low) return low;
            }
    if (!used) dis[loc]=-1;
    return used;
}
#define inf 0x7fffffff
int dinic()
{
    int tmp=0;
    while (bfs())
        {
            for (int i=S; i<=T; i++) cur[i]=head[i];
            tmp+=dfs(S,inf);
        }
    return tmp;
}
bool OK(int x,int y) {return (x>=1 && x<=N)&&(y>=1 && y<=M);}
int Get(int x,int y)
{
    int re=0;
    if (OK(x-1,y) && G[x-1][y]=='#') re++;
    if (OK(x+1,y) && G[x+1][y]=='#') re++;
    if (OK(x,y-1) && G[x][y-1]=='#') re++;
    if (OK(x,y+1) && G[x][y+1]=='#') re++;
    return re; 
}
int id[100][100],ID,col[100][100];
void init() {cnt=1; memset(head,0,sizeof(head)); ID=0;}
void BuildGraph()
{
    int Tot=0;
    init();
    for (int i=1; i<=N; i++)
        for (int j=1; j<=M; j++)
            id[i][j]=++ID,col[i][j]=(i+j)&1;
    S=0; T=ID+1;
    for (int i=1; i<=N; i++)
        for (int j=1; j<=M; j++)
            {
                if (G[i][j]=='#') Tot+=4-Get(i,j);
                if (G[i][j]=='?') if (Get(i,j)<2)  Tot+=4-2*Get(i,j);
            }
    for (int i=1; i<=N; i++)
        for (int j=1; j<=M; j++)
            if (G[i][j]=='?')
                if (col[i][j])
                    {
                        if (Get(i,j)<2) 
                            {
                                InsertEdge(S,id[i][j],4-2*Get(i,j));
                                if (OK(i-1,j) && G[i-1][j]=='?') InsertEdge(id[i][j],id[i-1][j],2);
                                if (OK(i+1,j) && G[i+1][j]=='?') InsertEdge(id[i][j],id[i+1][j],2);
                                if (OK(i,j-1) && G[i][j-1]=='?') InsertEdge(id[i][j],id[i][j-1],2);
                                if (OK(i,j+1) && G[i][j+1]=='?') InsertEdge(id[i][j],id[i][j+1],2);
                            }
                    }
                else 
                    if (Get(i,j)<2) InsertEdge(id[i][j],T,4-2*Get(i,j));
    int MaxFlow=dinic();
    printf("Case #%d: %d\n",Cas-cas,Tot-MaxFlow);
}
int main()
{
    Freopen();
    Cas=cas=read();
    while (cas--)
        {
            N=read(),M=read();
            for (int i=1; i<=N; i++)
                for (int j=1; j<=M; j++)
                    cin>>G[i][j];
            BuildGraph();
        }
    Fclose();
    return 0;
}

 

posted @ 2016-08-11 17:49  DaD3zZ  阅读(225)  评论(0编辑  收藏  举报