您的位置:首页 > 其它

hdu1814Peaceful Commission【2-SAT】输出最小解

2016-01-15 15:49 435 查看
居然1A了!!之前没调对居然只是多乘2了 (⊙﹏⊙)b 而且加边写反了==

题解:每个党派需要在两个代表中选一个,这2*n个代表中有彼此讨厌的m对人,输出n个去开会的代表

重点不在于怎么建边(建边这么so easy的事居然脑袋短路也是醉了==)在于怎么输出,网上的方法真的好麻烦,为什么非得染三个颜色的点啊我就不懂了,可能是模板的思路不好造成的吧


刘汝佳的书上说这个模板的思路是:逐一考虑没有赋值的变量(0~n-1范围内)即真、假未知的点,先假设这个变量是假(这个正好和题中要求一致,要是输出最大值就得先染色为真的结点了),然后顺着有向边标记所有能标记的点,如果发现对应为真的点被标记,那么“为假”的假定不成立,需要改成“为真”,然后重新标记。注意,这个算法没有回溯过程!。所以,当要重新标记时,之前那个点没有从0改成1.如果输出的“假”时候,需要保证“假”=1,“真”=0;其他情况
输出“真”

=============================分割线========================================

经测试,

for(int i=0;i<n;i++)
{
if(solver.mark[i<<1]) printf("%d\n",i<<1|1);
else printf("%d\n",(i<<1)+2);
}  

答案也对,其实我还是觉得是数据不严谨导致的,虽然poj3648也是只判断==1不用对点==0,因为算法没有回溯过程,只是判断一个点并不能保证这个点是真正的“真”



/***********
hdu1814
2016.1.15
998MS 2924K 1784 B G++
***********/
#include <iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
#define maxn 8004
struct TWOSAT
{
int n;
vector<int>G[maxn*2];
bool mark[maxn*2];
int S[maxn*2],c;
bool dfs(int x)
{
if(mark[x^1])return false;
if(mark[x]) return true;
mark[x]=true;
S[c++]=x;
for(int i=0;i<G[x].size();i++)
if(!dfs(G[x][i])) return false;
return true;
}
void init(int n)
{
this->n=n;
for(int i=0;i<n*2;i++) G[i].clear();
memset(mark,0,sizeof(mark));
}
void add_clause(int x,int y)
{
G[x].push_back(y^1);
G[y].push_back(x^1);///不一定是+1、-1!!而且本身就2n不用乘以2
}
bool solve()
{
for(int i=0;i<2*n;i+=2)
{
if(!mark[i]&&!mark[i+1])
{
c=0;
if(!dfs(i))
{
while(c>0) mark[S[--c]]=false;
if(!dfs(i+1)) return false;
}
}
}
return true;
}
}solver;
int n,m;

int main()
{
// freopen("cin.txt","r",stdin);
while(~scanf("%d%d",&n,&m))
{
solver.init(n);
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
a--;b--;
solver.add_clause(a,b);
}
if(!solver.solve()) puts("NIE");
else
{
for(int i=0;i<n;i++)
{
if(solver.mark[i<<1]&!solver.mark[i<<1|1]) printf("%d\n",i<<1|1);
else printf("%d\n",(i<<1)+2);
}
}
}
return 0;
}


算是为假期开个好头吧
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: