ACM-ICPC wf2014 金属加工厂 metal 单调性维护+2_SAT

来源:互联网 发布:网络与internet连不上 编辑:程序博客网 时间:2024/04/29 17:20

【问题描述】

  定义集合S的价值D(S)为:
  这里写图片描述
  现在给你n个元素,并给出其中任意两个元素之间的d(i,j)值,要你将这些元素划分成两个集合A、B。求min{D(A)+D(B)}。注:d(i,j)=d(j,i)。

【输入格式】

  输入数据的第一行是一个整数n,代表元素个数。
  之后n-1行描述的是d(i,j),这部分里,第i行包含n-i个整数,第i行第j列的整数代表的是d(i,i+j)。

【输出格式】

  输出只有一行,一个整数,代表min{D(A)+D(B)}。

【输入样例】

【样例1】
 5
 4 5 0 2
 1 3 7
 2 0
 4

【样例2】
 7
 1 10 5 5 5 5
 5 10 5 5 5
 100 100 5 5
 10 5 5
 98 99
 3

【输出样例】

【样例1】
 4

【样例2】
 15

【数据范围】

1 ≤ n ≤ 200
0<=d(i,i+j)<=10^9

——————————————————————————————————————————————————————

想到可以用2_SAT来判定,但是这是判定,关键是主程序的框架怎么写。
肯定要先把所有的边拿出来排序搞事情这个没话说的。
想了很久二分套二分,发现一点道理都没有。。。。
后来又想枚举一边二分另一边,嗯似乎很有道理的样子,但是仔细一想不对要TL。。。。(我家题库上时限1S简直。。QAQ)

已经发现可以枚举一边二分另一边了,那么就考虑一下维护两个单调指针。这个指针代表的是当前情况下两个集合中各自最大的边的权值。然后在2_SAT中把一个点拆成2个分别表示在第一个集合(一下简称0)和在第二个集合(以下简称1)的情况,然后就开始考虑建立2 _SAT的判定图。(可以证明这是满足单调性的,但是有一点复杂,主要是考虑一些特殊情况以及假设验证,同时2 _SAT判定到假解的影响是会被消除的,证了两节晚自习。。。)
左指针(以下简称L)右边的所有边连接的点都要连边0->1,右指针(以下简称R)右边的边连接的点都要连边1->0,然后在左右指针向右向左走的过程中就会出现L经过的地方删边,R经过的地方要加边,所以要动态维护(打标记会TL的很惨ORZ)。

但是这样还是会TL,要加一个剪枝,发现加的边连接的点在上一次判定成功的图中全都在不同集合中的时候就可以不用重新判定了。

然后就可以1S过了。

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<vector>#include<cctype>#define inf 2*1e9+5using namespace std;const int maxn=205;const int maxm=40005;int N,g[maxn][maxn];int ans=inf;bool vis[maxn<<1];struct _edge{ int from,to,w; }_E[maxm];struct edge{ int to,next; }E[maxm<<1];int first[maxn<<1],np,cnt,other[maxn<<1];int stk[maxn<<1],top;void data_in(){    scanf("%d",&N);    int x;    for(int i=1;i<N;i++)    for(int j=i+1;j<=N;j++)    {        scanf("%d",&x);        g[i][j]=g[j][i]=x;    }}bool cmp(_edge x,_edge y) { return x.w<y.w; }void add_edge(int u,int v){    E[++np]=(edge){v,first[u]};    first[u]=np;}void delete_edge(int u,int v){    int p=first[u],lastp=0;    while(p)    {        if(E[p].to==v) break;        lastp=p;        p=E[p].next;    }    if(lastp) E[lastp].next=E[p].next;    else first[u]=E[p].next;}void init(){    for(int i=1;i<N;i++)    for(int j=i+1;j<=N;j++)        _E[++cnt]=(_edge){i,j,g[i][j]};    sort(_E+1,_E+cnt+1,cmp);    for(int i=1;i<=N;i++) other[i]=i+N,other[i+N]=i;    for(int i=1;i<=cnt;i++)    {        int x=_E[i].from,y=_E[i].to;        add_edge(x,other[y]);        add_edge(y,other[x]);    }}bool DFS(int i){    vis[i]=1;    stk[++top]=i;    for(int p=first[i];p;p=E[p].next)    {        int j=E[p].to;        if(vis[j]) continue;        if(vis[other[j]]) return 0;        if(!DFS(j)) return 0;    }    return 1;}bool two_SAT(){    memset(vis,0,sizeof(vis));    for(int i=1;i<=N;i++) if(!vis[i]&&!vis[other[i]])    {        top=0;        if(!DFS(i))        {            while(top) vis[stk[top--]]=0;            if(!DFS(other[i])) return 0;        }    }    return 1;}void work100(){    init();    int L=0,R=cnt;    bool ok=0;    while(L<=R)    {        if(ok||two_SAT())        {            ok=1;            ans=min(ans,_E[L].w+_E[R].w);            add_edge(other[_E[R].from],_E[R].to);            add_edge(other[_E[R].to],_E[R].from);            if(!(vis[_E[R].from]&&vis[other[_E[R].to]]                ||vis[_E[R].to]&&vis[other[_E[R].from]])) ok=0;            R--;            while(R>L&&_E[R].w==_E[R+1].w)            {                add_edge(other[_E[R].from],_E[R].to);                add_edge(other[_E[R].to],_E[R].from);                if(!(vis[_E[R].from]&&vis[other[_E[R].to]]                    ||vis[_E[R].to]&&vis[other[_E[R].from]])) ok=0;                R--;            }        }        else        {            ok=0;            L++;            delete_edge(_E[L].from,other[_E[L].to]);            delete_edge(_E[L].to,other[_E[L].from]);            while(L<R&&_E[L].w==_E[L+1].w)            {                L++;                delete_edge(_E[L].from,other[_E[L].to]);                delete_edge(_E[L].to,other[_E[L].from]);            }        }    }    if(N==1||N==2) ans=0;    printf("%d\n",ans);}int main(){    freopen("test.in","r",stdin);    freopen("test.out","w",stdout);    data_in();    work100();    return 0;}
原创粉丝点击