本节内容——
有n架飞机需要着陆。每架飞机都可以选择“早着陆"和”晚着陆“两种方式之一,且必须选择一种。第i架飞机的早着陆时间为\(E_i\),晚着陆时间为\(L_I\),不得在其他时间着陆。现在需要安排这些飞机的着陆方式,使得整个着陆计划尽量安全。换句话说,如果把所有飞机的实际着陆时间按照从早到晚的顺序排列,相邻两个着陆时间间隔的最小值(成为安全间隔)尽量大。
也就是二分这个时间,然后判断该2-SAT是否有解。(因为这个间隔时间越小就也可能有合法解,反之越不可能,所以可以二分答案)
构图的时候,如果两个决策(比如说\(i_l,j_l\))间隔小于二分的答案,我们就给\(i_l,j_r\)和\(i_r,j_l\)连有向边。
然后跑tarjan判断就行了,如果同一个飞机的两个决策在一个强联通分量里面,就没有合法解了。
顺便一提,刘汝佳书上写的那个做法复杂度是假的qwq
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 4010
#define INF 0x3f3f3f3f
using namespace std;
int n,m,t,tot,cnt,top;
int head[MAXN],dfn[MAXN],low[MAXN],in[MAXN],st[MAXN],c[MAXN];
struct Edge{int nxt,to;}edge[MAXN*MAXN];
struct Node{int l,r;}node[MAXN];
inline void add(int from,int to){edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;}
inline void tarjan(int x)
{
dfn[x]=low[x]=++tot;
in[x]=1;
st[++top]=x;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
else if(in[v]) low[x]=min(low[x],dfn[v]);
}
if(dfn[x]==low[x])
{
int v;++cnt;
do{v=st[top--],c[v]=cnt,in[v]=0;}while(x!=v);
}
}
inline bool check(int x)
{
memset(head,0,sizeof(head));
t=tot=top=cnt=0;
for(int i=1;i<=(n>>1);i++)
for(int j=1;j<=(n>>1);j++)
{
if(i==j) continue;
int a=abs(node[i].l-node[j].l);
int b=abs(node[i].l-node[j].r);
int c=abs(node[i].r-node[j].l);
int d=abs(node[i].r-node[j].r);
if(a<x) add(i*2-1,j*2);
if(b<x) add(i*2-1,j*2-1);
if(c<x) add(i*2,j*2);
if(d<x) add(i*2,j*2-1);
}
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(in,0,sizeof(in));
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
for(int i=1;i<n;i+=2)
if(c[i]==c[i+1])
return false;
return true;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++) scanf("%d%d",&node[i].l,&node[i].r);
n<<=1;
int l=0,r=INF,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}
有A,B,C三种任务要分配给n个宇航员,其中每个宇航员恰好要分配一个任务。设所有n个宇航员的平均年龄为x,只有年龄大于或等于x的宇航员才能分配任务A;只有年龄严格小于x的宇航员才能分配任务B,而任务C没有限制。有m对宇航员相互讨厌,因此不能分配到统一任务。现在需要找出一个满足上诉所有要求的任务分配方案。
3-SAT???不可能的。我们只要处理一下年龄,对于每个宇航员,照样是2-SAT.
然后就......和上面那题一样做就行了啊??
但是为什么会RE啊......搞不懂......先把代码贴上,回来找锅(咕咕咕)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,m,all,kkk,cnt,tot,top,t;
int head[MAXN],done[MAXN];
int dfn[MAXN],low[MAXN],in[MAXN],st[MAXN],c[MAXN];
char op[MAXN];
struct Node{int l,r,age;}node[MAXN];
struct Edge{int nxt,to;}edge[MAXN<<1];
struct Line{int u,v;}line[MAXN<<1];
inline void add(int from,int to){edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;}
inline void tarjan(int x)
{
low[x]=dfn[x]=++tot;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
else if(in[v]) low[x]=min(low[x],dfn[v]);
}
if(low[x]==dfn[x])
{
int v;++cnt;
do{v=st[top--];in[v]=0;c[v]=++cnt;}while(x!=v);
}
}
inline bool check()
{
memset(head,0,sizeof(head));
memset(in,0,sizeof(in));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
top=tot=t=cnt=0;
for(int i=1;i<=m;i++)
{
int u=line[i].u,v=line[i].v;
if(node[u].age^node[v].age)
{
add(node[u].r,node[v].l),printf("%d %d\n",node[u].r,node[v].l);
add(node[v].r,node[u].l),printf("%d %d\n",node[v].r,node[u].l);
}
else
{
add(node[u].l,node[v].r),printf("%d %d\n",node[u].l,node[v].r);
add(node[u].r,node[v].l),printf("%d %d\n",node[u].r,node[v].l);
add(node[v].l,node[u].r),printf("%d %d\n",node[v].l,node[u].r);
add(node[v].r,node[u].l),printf("%d %d\n",node[v].r,node[u].l);
}
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i),cout<<i<<endl;
for(int i=1;i<=n;i++)
if(c[node[i].l]==c[node[i].r]&&c[node[i].l]!=0)
return false;
return true;
}
inline bool solve(int x,int c)
{
done[x]=c,done[x^1]=3-c;
printf("done[%d]=%d done[%d]=%d\n",x,done[x],x^1,done[x^1]);
cout<<endl;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(done[v]&&done[v]==c) return false;
else if(!done[v]) solve(v,c);
}
return true;
}
inline void print()
{
cout<<"yes"<<endl;
for(int i=1;i<=n;i++)
{
if(done[node[i].l]==1) printf("%c\n",op[node[i].l]);
else if(done[node[i].l]==0&&done[node[i].r]==0) printf("%c\n",op[node[i].l]);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
while(scanf("%d%d",&n,&m)==2)
{
if(n==0&&m==0) break;
memset(head,0,sizeof(head));
top=tot=t=cnt=all=0;
for(int i=1;i<=n;i++) scanf("%d",&node[i].age),all+=node[i].age;
all/=n;
for(int i=1;i<=n;i++)
{
if(node[i].age<all) node[i].age=0;
else node[i].age=1;
}
for(int i=1;i<=n;i++) scanf("%d%d",&line[i].u,&line[i].v);
for(int i=1;i<=n;i++)
if(node[i].age==0)
node[i].l=++kkk,op[kkk]='B',node[i].r=++kkk,op[kkk]='C';
else
node[i].l=++kkk,op[kkk]='A',node[i].r=++kkk,op[kkk]='C';
for(int i=1;i<=n;i++)
printf("%c %c\n",op[node[i].l],op[node[i].r]);
if(check()==false) {printf("No solution.\n");continue;}
memset(done,0,sizeof(done));
bool flag=true;
for(int i=1;i<=n;i++)
if(!done[i])
if(solve(node[i].l,1))
flag=false;
if(flag==true) {print();continue;}
memset(done,0,sizeof(done));
flag=true;
for(int i=1;i<=n;i++)
if(!done[i])
solve(node[i].r,1);
print();
}
return 0;
}
机场快线分为经济线和商业线两种,线路、速度和价钱都不同。现在你有一张商业线的车票,可以坐一站商业线,而其他时候只能乘坐经济线。假设换成时间忽略不计,你的任务是找一条取机场最快的线路,然后输出方案。(保证最优解唯一)
因为商业线只能坐一站,而且数据范围在1000以内,所以我们可以枚举坐的是哪一站。
假设我们用商业线车票从车站a坐到b,则从起点到a,从b到终点这两部分的路线对于只存在经济线的图中一定是最短路。所以我们只需要从起点、终点开始做两次最短路,记录下从起点到每个点x的最短时间\(f(x)\)和它到终点的最短时间\(g(x)\),那么总时间就是\(f(a)+time(a,b)+g(b)\)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000;
const int maxn = 500 + 10;
int T,n,m,S,d1[maxn], p1[maxn], d2[maxn], p2[maxn], vis[maxn], ok, dist;
struct node {
int u, val;
node(int u=0, int val=0):u(u), val(val) {}
bool operator < (const node& rhs) const {
return val > rhs.val;
}
};
void print(int root, int p[], int id, int S) {
vector<int> ans;
while(root != S) {
ans.push_back(root);
root = p[root];
}
ans.push_back(root);
int len = ans.size();
if(id == 1) for(int i=len-1;i>=0;i--) {
if(i != len-1) printf(" ");
printf("%d",ans[i]);
}
else for(int i=0;i<len;i++) {
if(i != 0) printf(" ");
printf("%d",ans[i]);
}
}
vector<node> g[maxn];
void BFS(int haha, int d[], int p[]) {
priority_queue<node> q;
q.push(node(haha, 0));
for(int i=1;i<=n;i++) {
d[i] = INF;
}
d[haha] = 0;
memset(vis, false, sizeof(vis));
while(!q.empty()) {
node u = q.top(); q.pop();
if(vis[u.u]) continue;
vis[u.u] = true;
int len = g[u.u].size();
for(int i=0;i<len;i++) {
node v = g[u.u][i];
if(d[v.u] > d[u.u] + v.val) {
d[v.u] = d[u.u] + v.val;
p[v.u] = u.u;
q.push(node(v.u, d[v.u]));
}
}
}
}
int a,b,c,kase=0;
int main() {
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
while(~scanf("%d%d%d",&n,&S,&T)) {
scanf("%d",&m);
for(int i=1;i<=n;i++) g[i].clear();
while(m--) {
scanf("%d%d%d",&a,&b,&c);
g[a].push_back(node(b, c));
g[b].push_back(node(a, c));
}
scanf("%d",&m);
ok = -1;
BFS(S, d1, p1);
BFS(T, d2, p2);
int s = -1,t = -1,ans = d1[T], res = 0;
for(int i=0;i<m;i++) {
scanf("%d%d%d",&a,&b,&c);
if(cur < ans) {
ans = cur;
s = b; t = a;
}
}
if(kase) printf("\n");
else ++kase;
if(s > 0) {
print(s, p1, 1, S); printf(" ");
print(t, p2, 2, T); printf("\n");
printf("%d\n",s);
}
else {
print(T, p1, 1, S); printf("\n");
printf("Ticket Not Used\n");
}
printf("%d\n",ans);
}
return 0;
}
书上还提到了dij算法的路径统计,在这里就简单说一下吧
枚举两点之间的所有最短路:先求出所有点到目标点的最短路长度\(d[i]\),然后从起点开始出发,只沿着\(d[i]=d[j]+dist(i,j)\)的边走。
两点之间的最短路计数:令\(f[i]\)表示从i到目标点的最短路的条数,则\(f[i]=\sum f[j] | d[i]=d[j]+dist(i,j)\)(这里书上写错了)
对于一张图,只沿着满足如下条件的道路(A,B)走:存在一条从B出发回家的路径,比所有从A出发回家的路径都短。问不同的回家路径条数。
先跑完以家为源点的最短路,然后如果一个点a的最短路比b的小,那么连边,这就是一个DAG了,然后再DP计个数就行了。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 100010
using namespace std;
int n,m,t;
int head[MAXN<<1],done[MAXN],dis[MAXN],dp[MAXN];
struct Node
{
int u,d;
friend bool operator < (Node x,Node y)
{return x.d>y.d;}
};
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
struct Line{int u,v,w;}line[MAXN<<1];
inline void add(int from,int to,int dis)
{
edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
}
inline void dij(int x)
{
priority_queue<Node>q;
memset(done,0,sizeof(done));
memset(dis,0x3f,sizeof(dis));
q.push((Node){x,0});
dis[x]=0;
while(!q.empty())
{
int u=q.top().u;q.pop();
if(done[u]) continue;
done[u]=1;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].dis)
dis[v]=dis[u]+edge[i].dis,q.push((Node){v,dis[v]});
}
}
}
inline int solve(int x)
{
if(x==2) return dp[x]=1;
int cur_ans=0;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(!dp[v]) dp[v]=solve(v);
cur_ans+=dp[v];
}
return cur_ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
while(scanf("%d%d",&n,&m)==2&&n)
{
memset(head,0,sizeof(head));
memset(dp,0,sizeof(dp));
t=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&line[i].u,&line[i].v,&line[i].w);
add(line[i].u,line[i].v,line[i].w);
add(line[i].v,line[i].u,line[i].w);
}
dij(2);
// for(int i=1;i<=n;i++) printf("dis[%d]=%d\n",i,dis[i]);
memset(head,0,sizeof(head));
t=0;
for(int i=1;i<=m;i++)
{
if(dis[line[i].u]>dis[line[i].v]) add(line[i].u,line[i].v,line[i].w);
if(dis[line[i].v]>dis[line[i].u]) add(line[i].v,line[i].u,line[i].w);
}
printf("%d\n",solve(1));
}
return 0;
}
最短路树:用dij算法可以求出单元最短路树,方法是在发现\(d[i]+w(i,j)<d[j]\)时除了更新\(d[j]\)之外还要设置\(p[j]=i\)。这样,所有点就形成了一棵树。
要从起点出发沿着最短路走到其他任意点,只需要顺着树上的边走即可。
给出一个n个节点m条边的无向图,
原文:https://www.cnblogs.com/fengxunling/p/10851094.html