文章目录
HDU3062Party
2-SAT的模板题。建边的时候注意对称性。如果a和b矛盾,那么a->b’,b->a’
#include <iostream>
#include <string>
#include <stack>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int maxn = 10000;
const int maxm = 2000200;
int head[maxn],instack[maxn],id[maxn],low[maxn] , dfn[maxn];
int ct , idx;
int tot;
int a1,a2,b1,b2;
int n,m;
stack<int>S;
void init(int n)
{
ct = idx = tot = 0;;
while(!S.empty())S.pop();
for(int i = 0;i<=2*n;i++){
head[i] = -1;
instack[i] = 0;
dfn[i] = low[i] = 0;
}
}
struct _edge{
int v;
int next;
}edge[maxm];
void addedge(int u,int v)
{
edge[ct].v = v;
edge[ct].next = head[u];
head[u] = ct++;
}
void tar(int now)
{
dfn[now] = low[now] = ++idx;
S.push(now);
instack[now] = 1;
for(int i = head[now];~i;i = edge[i].next)
{
int to = edge[i].v;
if(!dfn[to]){
tar(to);
low[now] = min(low[now],low[to]);
}else if(instack[to]){
low[now] = min(low[now] , dfn[to]);
}
}
if(low[now] == dfn[now])
{
tot ++;
while(S.top() != now)
{
id[S.top()] = tot;
instack[S.top()] = 0;
S.pop();
}
id[now] = tot;
instack[now] = 0;
S.pop();
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init(n);
for(int i = 0;i<m;i++)
{
scanf("%d%d%d%d",&a1,&a2,&b1,&b2);
int u1 = 2*a1 + b1;
int v1 = 2*a2 + 1 - b2;
int u2 = 2*a2 + b2;
int v2 = 2*a1 + 1 - b1;
addedge(u1,v1);
// addedge(v1,u1);
addedge(u2,v2);
// addedge(v2,u2);
}
for(int i = 0;i<2*n;i++)
{
if(!dfn[i]){
tar(i);
}
}
bool flag = true;
for(int i = 0;i<n;i++)
{
if(id[2*i] == id[2*i + 1]){
flag = false;
break;
}
}
if(flag)printf("YES\n");
else printf("NO\n");
}
}
POJ3683
每场婚礼只能选两个时间点,问能不能找出一种安排,使得每场都参加,且时间点不冲突。
2-SAT,需要
n
2
n^{2}
n2找出哪些时间点之间是矛盾的,根据矛盾关系渐边,然后2SAT,将缩点后标号小的输出。
#include <iostream>
#include <stack>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int maxn = 10020;
const int maxm = 32000020;
int head[maxn],instack[maxn],id[maxn] , low[maxn] , dfn[maxn];
int ct , tot , idx;
int n,m;
int aa,bb,cc,dd,ee;
stack<int>S;
struct _edge{
int v;
int next;
}edge[maxm];
void init(int n)
{
while(!S.empty())S.pop();
ct = tot = idx = 0;
for(int i = 0;i<=n;i++)
{
head[i] = -1;
instack[i] = id[i] = dfn[i] = low[i] = 0;
}
}
void addedge(int u,int v)
{
edge[ct].v = v;
edge[ct].next = head[u];
head[u] = ct++;
}
void tar(int now)
{
low[now] = dfn[now] = ++idx;
S.push(now);
instack[now] = true;
for(int i = head[now];~i;i = edge[i].next)
{
int to = edge[i].v;
if(!dfn[to]){
tar(to);
low[now] = min(low[now] , low[to]);
}else if(instack[to])
{
low[now] = min(low[now] , dfn[to]);
}
}
if(low[now] == dfn[now])
{
++tot;
while(S.top()!=now)
{
id[S.top()] = tot;
instack[S.top()] = false;
S.pop();
}
id[S.top()] = tot;
instack[S.top()] = false;
S.pop();
}
}
struct te{
int St;
int Et;
int w;
}Te[maxm];
bool check(te ati , te bti)
{
if(min(ati.Et , bti.Et) > max(ati.St , bti.St))return false;
return true;
}
int main()
{
while(~scanf("%d",&n))
{
init(2*n);
for(int i = 0;i<n;i++)
{
scanf("%d:%d%d:%d%d",&aa,&bb,&cc,&dd,&ee);
Te[2*i].St = aa *60 + bb ;
Te[2*i].Et = aa * 60 + bb +ee;
Te[2*i + 1].St = cc * 60 + dd - ee;
Te[2*i + 1].Et = cc * 60 + dd;
Te[2*i].w = Te[2*i + 1].w = ee;
}
for(int i = 0;i<n;i++){
for(int j = i + 1;j<n;j++){
if(!check(Te[2*i] , Te[2*j])){
addedge(2*i , 2*j + 1);
addedge(2*j , 2*i + 1);
}
if(!check(Te[2*i] , Te[2*j + 1])){
addedge(2*i , 2*j);
addedge(2*j + 1,2*i+1);
}
if(!check(Te[2*i+1] , Te[2*j])){
addedge(2*i+1 , 2*j+1);
addedge(2*j , 2*i);
}
if(!check(Te[2*i+1],Te[2*j+1])){
addedge(2*i+1 , 2*j);
addedge(2*j+1 , 2*i);
}
}
}
for(int i = 0;i<2*n;i++)
{
if(!dfn[i]){
tar(i);
}
}
bool flag = true;
for(int i = 0;i<n;i++)
{
if(id[2*i] == id[2*i + 1]){
flag = false;
break;
}
}
if(!flag){
printf("NO\n");
continue;
}
printf("YES\n");
for(int i = 0;i<n;i++)
{
if(id[2*i] < id[2*i+1]){
printf("%02d:%02d %02d:%02d\n" , Te[2*i].St/60 , Te[2*i].St % 60 , Te[2*i].Et / 60 , Te[2*i].Et % 60);
}else{
printf("%02d:%02d %02d:%02d\n" , Te[2*i+1].St/60 , Te[2*i+1].St % 60 , Te[2*i+1].Et / 60 , Te[2*i+1].Et % 60);
}
}
}
}
Wormholes 虫洞 HYSBZ - 1715
spfa判环,没啥说的。
#include <iostream>
#include <queue>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1020;
const int maxm = 10020;
int head[maxn] , cnt[maxn] , inq[maxn] , dis[maxn];
int ct;
struct _edge{
int v;
int w;
int next;
}edge[maxm];
void addedge(int u,int v ,int w)
{
edge[ct].v = v;
edge[ct].w = w;
edge[ct].next = head[u];
head[u] = ct++;
}
void init(int n)
{
for(int i = 0;i<=n;i++){
head[i] = -1;
cnt[i] = 0;
inq[i] = 0;
dis[i] = INF;
}
ct = 0;
}
int n,m,mm,u,v,w,aa,bb,cc;
bool spfa(int st)
{
queue<int>Q;
Q.push(st);
inq[st] = true;
cnt[st] = 1;
dis[st] = 0;
while(!Q.empty())
{
int now = Q.front();
Q.pop();
inq[now] = false;
for(int i = head[now];~i;i = edge[i].next)
{
int to = edge[i].v;
if(dis[to] > dis[now] + edge[i].w )
{
dis[to] = dis[now] + edge[i].w;
if(!inq[to]){
Q.push(to);
inq[to] = true;
cnt[to]++;
if(cnt[to] > n)return false;
}
}
}
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&mm);
init(n);
for(int i = 0;i<m;i++){
scanf("%d%d%d" , &u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
for(int i = 0;i<mm;i++){
scanf("%d%d%d" , &u,&v,&w);
addedge(u,v,-w);
}
if(spfa(1)){
printf("NO\n");
}else{
printf("YES\n");
}
}
}
糖果 HYSBZ - 2330
一道非常经典的查分约束,我用的最长路,得到的是满足条件的最小值。同时要记得加每个糖果大于0的约束。
可以好好做一下,包含了各种可能,以后差分约束的裸题都不怕对了。
#include <iostream>
#include <stack>
#include <algorithm>
#include <stdio.h>
#include <queue>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 100020;
const int maxm = 320020;
int head[maxn] , cnt[maxn] ;
ll dis[maxn];
bool inq[maxn];
int ct , tot , idx;
int n,m,k;
int aa,bb,cc,dd,ee;
struct _edge{
int v;
ll w;
int next;
}edge[maxm];
void init(int n)
{
ct = 0;
for(int i = 0;i<=n;i++)
{
head[i] = -1;
dis[i] = -INF;
cnt[i] = 0;
inq[i] = 0;
}
}
void addedge(int u,int v,ll w)
{
edge[ct].v = v;
edge[ct].w = w;
edge[ct].next = head[u];
head[u] = ct++;
}
bool spfa(int now)
{
queue<int>Q;
dis[now] = 0ll;
for(int i = 1;i<=n;i++)Q.push(i) , dis[i] = 1ll , inq[i] = true , cnt[i] = 1;
while(!Q.empty())
{
int t = Q.front();
Q.pop();
inq[t] = false;
cnt[t]++;
for(int i = head[t];~i;i = edge[i].next)
{
int to = edge[i].v;
if(dis[to] < dis[t] + edge[i].w)
{
dis[to] = edge[i].w + dis[t];
if(!inq[to])
{
Q.push(to);
inq[to] = true;
cnt[to]++;
if(cnt[to] > n+2)return false;
}
}
}
}
return true;
}
int main()
{
scanf("%d%d",&n,&k);
init(n);
for(int i = 0;i<k;i++)
{
scanf("%d%d%lld",&aa,&bb,&cc);
if(aa == 1){
addedge(bb,cc,0);
addedge(cc,bb,0);
}else if(aa == 2){
if(bb == cc){
printf("-1\n");
return 0;
}
addedge(bb,cc,1);
}else if(aa == 3){
addedge(cc,bb,0);
}else if(aa == 4){
if(bb == cc){
printf("-1\n");
return 0;
}
addedge(cc,bb,1);
}else if(aa == 5){
addedge(bb,cc,0);
}
}
for(int i = 1;i<=n;i++)addedge(0,i,1);
if(spfa(0))
{
ll sum = 0;
for(int i = 1;i<=n;i++)sum += dis[i];
printf("%lld\n",sum);
}
else
{
printf("-1\n");
}
}
POJ 1364 King
给你一些连续序列的和的限制,问你能不能找到满足限制的序列情况
比如
a
i
+
a
i
+
1
+
.
.
.
+
a
j
−
1
+
a
j
>
k
a_{i}+a_{i+1}+...+a_{j-1}+a_{j} > k
ai+ai+1+...+aj−1+aj>k,我们考虑其前缀和序列
p
r
e
[
i
]
pre[i]
pre[i],刚才的限制其实就是
p
r
e
[
j
]
−
p
r
e
[
i
−
1
]
>
k
pre[j] - pre[i-1] > k
pre[j]−pre[i−1]>k,也就变成了一道差分约束的题
#include <iostream>
#include <string>
#include <stack>
#include <algorithm>
#include <stdio.h>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 10000;
const int maxm = 2000200;
int head[maxn];
int ct;
int m,n;
int dis[maxn] , cnt[maxn];
bool inq[maxn];
void init(int n)
{
for(int i = 0;i<=n;i++){
head[i] = -1;
dis[i] = INF;
cnt[i] = 0;
inq[i] = false;
}
ct = 0;
}
struct _edge{
int v;
int w;
int next;
}edge[maxm];
void addedge(int u,int v,int w)
{
edge[ct].v = v;
edge[ct].w = w;
edge[ct].next = head[u] ;
head[u] = ct++;
}
bool spfa()
{
queue<int>Q;
for(int i = 0;i<=n;i++)
{
inq[i] = true;
cnt[i] = 1;
dis[i] = 1;
Q.push(i);
}
while(!Q.empty())
{
int now = Q.front();
Q.pop();
inq[now] = false;
for(int i = head[now];~i;i = edge[i].next)
{
int to = edge[i].v;
if(dis[to] > dis[now] + edge[i].w)
{
dis[to] = dis[now] + edge[i].w;
if(!inq[to]){
inq[to] = true;
Q.push(to);
cnt[to] ++;
if(cnt[to] > n )return false;
}
}
}
}
return true;
}
char ts[5];
int aa,bb,cc;
int main()
{
while(~scanf("%d",&n) && n)
{
scanf("%d",&m);
init(n);
for(int i = 0;i<m;i++)
{
//cout << "fuck" << endl;
scanf("%d %d %s %d",&aa,&bb,ts,&cc);
if(ts[0] == 'g')
{
addedge(aa + bb , aa-1 , -cc - 1);
}else{
addedge(aa-1 , aa + bb , cc - 1);
}
}
if(!spfa()){
printf("successful conspiracy\n");
}
else{
printf("lamentable kingdom\n");
}
}
}
HDU 2063 过山车
二分图最大匹配
#include <iostream>
#include <stack>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <string>
#include <queue>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 1020;
const int maxm = 10020;
int head[maxn];
int vis[maxn];
int matching[maxn];
int ct , tot , idx;
int n,m,k;
int aa,bb,cc,dd,ee;
int u,v;
struct _edge{
int v;
int w;
int next;
}edge[maxm];
void init(int n)
{
ct = 0;
for(int i = 0;i<=n;i++)
{
head[i] = matching[i] = -1;
vis[i] = 0;
}
}
void addedge(int u,int v)
{
edge[ct].v = v;
edge[ct].next = head[u];
head[u] = ct++;
}
bool findp(int now)
{
for(int i = head[now];~i;i = edge[i].next)
{
int to = edge[i].v;
if(!vis[to])
{
vis[to] = true;
if(matching[to] == -1 || findp(matching[to]))
{
matching[now] = to;
matching[to] = now;
return true;
}
}
}
return false;
}
int hung(int n)
{
int ans = 0;
for(int i = 1;i <= n ;i++)
{
if(matching[i] == -1){
memset(vis,0,sizeof(vis));
if(findp(i))ans++;
}
}
return ans;
}
int main()
{
while(~scanf("%d",&k) && k)
{
scanf("%d%d" , &m,&n);
init(m + n);
for(int i = 0;i<k;i++){
scanf("%d%d",&u,&v);
addedge(u,v+m);
Asteroids POJ - 3041
矩阵中有许多星球,每次可以扎一行或者炸一列,问至少要炸多少次可以炸了所有星球
考虑行列建图,对于任何一个星球,行代表的点和列代表的点之间见一个图,这条边就是这个星球,炸了这个星球只需要覆盖这条边的其中一个点即可。我们要做的就是选取尽可能少的点,覆盖所有边。
那么就是一个求最小点覆盖的题,数量同最大匹配。
#include <iostream>
#include <string>
#include <string>
#include <string.h>
#include <stack>
#include <algorithm>
#include <stdio.h>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 250020;
const int maxm = 200020;
int head[maxn];
int ct;
int m,n;
int aa,bb;
int vis[maxn] , cnt[maxn];
int matching[maxn];
void init(int n)
{
for(int i = 0;i<=n;i++){
head[i] = -1;
matching[i] = -1;
}
ct = 0;
}
struct _edge{
int v;
int w;
int next;
}edge[maxm];
void addedge(int u,int v)
{
edge[ct].v = v;
edge[ct].next = head[u] ;
head[u] = ct++;
}
bool findp(int now)
{
for(int i = head[now];~i;i = edge[i].next)
{
int to = edge[i].v;
if(!vis[to])
{
vis[to] = true;
if(matching[to] == -1 || findp(matching[to])){
matching[now] = to;
matching[to] = now;
return true;
}
}
}
return false;
}
int hung()
{
int ans = 0;
for(int i = 1;i<=2*n;i++)
{
if(matching[i] == -1){
memset(vis,0,sizeof(vis));
if(findp(i))ans++;
}
}
return ans;
}
int main()
{
while(~scanf("%d%d" , &n,&m))
{
init(n * n);
for(int i = 0;i<m;i++)
{
scanf("%d%d",&aa,&bb);
addedge(aa , bb + n);
addedge(bb+n , aa);
}
printf("%d\n" , hung());
}
}