HDU1285 确定比赛名次
思路
简单拓扑排序模板
注意:答案不唯一,输出字典序小的
我这里用的是小根堆维护
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<int, vector<int>, greater<int> > heap;
const int N = 510, M = N * 2;
int q[N];
int h[N], ne[M], e[M], idx;
// 入度
int din[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int main() {
int n, m;
while(~scanf("%d%d", &n, &m)) {
memset(h, -1, sizeof h);
idx = 0;
memset(din, 0, sizeof din);
while(heap.size())
heap.pop();
while(m --) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
din[b]++;
}
for (int i = 1; i <= n; i ++)
if(!din[i])
heap.push(i);
bool flag = false; // 输出空格处理
while(heap.size()) {
int t = heap.top();
if(!flag) {
printf("%d", t);
flag = true;
}
else printf(" %d", t);
heap.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if(-- din[j] == 0)
heap.push(j);
}
}
printf("\n");
}
}
HDU - 2647 Reward
思路
简单的差分约束,因为边权是正权,用拓扑序列即可。
建图:若A>B, 则A>=B + 1, 即B向A连一条权值为1的边。
题目取最小值,用最长路。
为什么取最小选择最长路呢,举个例子就知道了
a >= 3
a >= 5
a >= 7
满足上列不等式的同时 取最小值,当前a = 7时是最小的。
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 10010, M = 20010;
typedef long long LL;
int q[N];
int h[N], e[M], ne[M], idx;
int dist[N];
int din[N];
int n, m;
void init() {
memset(h, -1, sizeof h);
idx = 0;
memset(din, 0, sizeof din);
}
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 判断是否有解
bool topsort() {
int hh = 0, tt = -1;
for (int i = 1; i <= n; i ++)
if(!din[i])
q[++tt] = i;
while(hh <= tt) {
int t = q[hh++];
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if(-- din[j] == 0)
q[++tt] = j;
}
}
return tt == n - 1;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
init();
while(m --) {
int a, b;
scanf("%d%d", &a, &b);
add(b, a);
din[a]++;
}
if(!topsort())
puts("-1");
else {
// 超级源点
for (int i = 1; i <= n; i ++)
dist[i] = 888;
// 拓扑图求最长路
for (int i = 0; i < n; i ++) {
int j = q[i];
for (int k = h[j]; ~k; k = ne[k]) {
dist[e[k]] = max(dist[e[k]], dist[j] + 1);
}
}
LL ans = 0;
for (int i = 1; i <= n; i ++) {
ans += dist[i];
}
printf("%lld\n", ans);
}
}
return 0;
}
HDU - 3342 Legal or Not
思路
拓扑排序,判断有向图是否存在环问题
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 110, M = 210;
int h[N], e[M], ne[M], idx;
int q[N];
int din[N];
int n, m;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool topsort() {
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++) {
if(!din[i])
q[++tt] = i;
}
while(hh <= tt) {
int t = q[hh++];
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if(-- din[j] == 0)
q[++tt] = j;
}
}
return tt == n - 1;
}
int main() {
while(~scanf("%d%d", &n, &m) && n) {
memset(h, -1, sizeof h);
idx = 0;
memset(din, 0, sizeof din);
while(m --) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
din[b]++;
}
if(topsort())
puts("YES");
else
puts("NO");
}
return 0;
}
HDU - 1811 Rank of Tetris
思路
- 先利用并查集将相等
即"="
的点进行缩点,即只对一个集合的祖先节点进行操作 - 跑一遍topsort,若过程中存在两个及以上入度为0的,若不conflict的话那就是uncertain
- 若拓扑排序入队的点 不等于缩点后的数量,说明存在环,及冲突conflict
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 10010, M = 20010;
int h[N], e[M], ne[M], idx;
int din[N];
int p[N];
int n, m;
int x[M], y[M];
char op[M];
int cnt;
int uflag, cflag;
void init() {
memset(h, -1, sizeof h);
memset(din, 0, sizeof din);
for (int i = 0; i < N; i ++)
p[i] = i;
idx = 0;
cnt=0;
uflag = 0; // 是否不确定
cflag = 0; // 是否冲突
}
int find(int x) {
if(p[x] != x)
p[x] = find(p[x]);
return p[x];
}
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void topsort() {
queue<int> q;
for (int i = 0; i < n; i ++) {
if(!din[i] && p[i] == i)
cnt++, q.push(i);
}
while(q.size()) {
if(q.size() > 1)
uflag = 1;
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if(-- din[j] == 0) {
q.push(j);
cnt++;
}
}
}
if(cnt != n)
cflag = 1;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
init();
for (int i = 1; i <= m; i ++) {
cin >> x[i] >> op[i] >> y[i];
if(op[i] == '=') {
int px = find(x[i]), py = find(y[i]);
if(px != py) {
cnt ++; // 先统计在同一集合的点的数量,不包括祖先节点, 之后所有祖先节点进行拓扑, 再加上祖先判断cnt是否等于n即可
p[px] = py;
}
}
}
for (int i = 1; i <= m; i ++) {
if(op[i] == '=')
continue;
int px = find(x[i]), py = find(y[i]);
if(op[i] == '<') {
add(px, py);
din[py]++;
}
else {
add(py, px);
din[px]++;
}
}
topsort();
if(cflag)
puts("CONFLICT");
else if(uflag) puts("UNCERTAIN");
else
puts("OK");
}
return 0;
}
POJ - 1094 Sorting It All Out
思路
和上题类似。topsort。其他细节在代码中揭晓
代码(参考作者)
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstdio>
using namespace std;
const int N = 30;
int n, m;
int cnt, kind, d[N], din[N];
bool st[N];
void topsort(int x, vector<int> g[]) {
queue<int> q;
string res;
bool flag = true;
memcpy(d, din, sizeof din);
for (int i = 0; i < n; i ++)
if(!d[i])
q.push(i);
while(q.size()) {
if(q.size() > 1)
flag = false;
int t = q.front();
q.pop();
res += 'A' + t;
vector<int>::iterator it = g[t].begin();
for (; it != g[t].end(); it ++) {
if(-- d[*it] == 0)
q.push(*it);
}
}
// 若答案长度 == n 并且未出现两个及以上同时入度为0的点
if(res.length() == n && flag) {
kind = 1;
cout << "Sorted sequence determined after " << x << " relations: " << res << ".\n";
}
// 这种情况, 存在环, 即冲突
if(res.length() < cnt) {
kind = 2;
printf("Inconsistency found after %d relations.\n", x);
}
}
int main() {
while(~scanf("%d%d", &n, &m), n || m) {
memset(din, 0, sizeof din);
memset(st, 0, sizeof st);
cnt = kind = 0;
// cnt表示当前加的点数量
vector<int> g[N];
for (int i = 1; i <= m; i ++) {
string str;
cin >> str;
if(kind)
continue;
int a = str[0] - 'A', b = str[2] - 'A';
// 标记, 防止重复计算点
if(!st[a]) {
st[a] = true;
cnt++;
}
if(!st[b]) {
st[b] = true;
cnt++;
}
din[b]++;
// 加边
g[a].push_back(b);
// 每次迭代跑topsort
topsort(i, g);
}
if(!kind)
puts("Sorted sequence cannot be determined.");
}
return 0;
}
POJ - 2367 Genealogical tree
题意
依次给定1~n的孩子,使每个人的孩子都比那个人后列出。
思路
拓扑排序模板题
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 110, M = N * N / 2;
int h[N], e[M], ne[M], idx;
int din[N];
int q[N];
int n;
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void topsort(){
int hh = 0, tt = -1;
for(int i = 1; i <= n; i ++){
if(!din[i]) q[++ tt] = i;
}
while(hh <= tt){
int t = q[hh ++];
for(int i = h[t]; ~i; i = ne[i]){
int j = e[i];
if(-- din[j] == 0) q[++ tt] = j;
}
}
}
int main(){
memset(h, -1, sizeof h);
cin >> n;
for(int i = 1; i <= n; i ++){
int x;
while(cin >> x, x){
add(i, x);
din[x] ++;
}
}
topsort();
for(int i = 0; i < n; i ++){
cout << q[i] << ' ';
}
return 0;
}
POJ - 1270 Following Orders
题意
输出所有的拓扑序列
思路
拓扑排序和dfs相结合。
在拓扑内进行递归,并记录路径即可。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 26 + 5;
char str[N], ans[N], apa[N];
int din[N], gra[N][N], res;
map<char, int> mp;
void topsort(int u) {
if(u == res) {
printf("%s\n", ans);
return;
}
for (int i = 0; i < res; i ++) {
if(din[i] == 0) {
// 相当于st[i] = true;
--din[i];
// 记录路径
ans[u] = apa[i];
// 释放由该点产生的入度
for (int j = 0; j < res; j ++) {
if(gra[i][j])
--din[j];
}
// 递归
topsort(u + 1);
// 回溯
++din[i];
for (int j = 0; j < res; j ++) {
if(gra[i][j])
++din[j];
}
}
}
}
int main() {
int flag, k, len;
char temp1, temp2;
while(gets(str)) {
// 初始化
k = 0;
memset(ans, 0, sizeof ans);
memset(din, 0, sizeof din);
memset(gra, 0, sizeof gra);
// 记录序列并进行映射
len = strlen(str);
for (int i = 0; i < len; i ++)
// 忽略空格
if(str[i] >= 'a' && str[i] <= 'z')
apa[k++] = str[i];
// 先排序, 保证字典序最小
sort(apa, apa + k);
for (int i = 0; i < k; i ++)
mp[apa[i]] = i;
res = k;
flag = 1;
// 连边, 还是输入恶心
gets(str);
len = strlen(str);
for (int i = 0; i < len; i ++)
if(str[i] >= 'a' && str[i] <= 'z') {
if(flag) {
temp1 = str[i];
flag = 0;
}
else {
temp2 = str[i];
gra[mp[temp1]][mp[temp2]] = 1;
din[mp[temp2]]++;
flag = 1;
}
}
topsort(0);
printf("\n");
}
return 0;
}
POJ - 3553 Task schedule
思路
贪心+topsort
贪心:按结束早的先处理
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
using namespace std;
const int N = 50010, M = 10 * N;
struct Node {
// 完成时间和结束时间
int t, e, id;
bool operator < (const Node &E)const {
return e < E.e;
}
} job[N];
int n;
int h[N], e[M], ne[M], idx;
int din[N];
int ans[N];
int cnt;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void init() {
memset(job, 0, sizeof job);
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++)
job[i].id = i;
cnt = 0;
}
void topsort() {
priority_queue<Node> q;
for (int i = 1; i <= n; i ++)
if(!din[i]) {
q.push(job[i]);
}
while(q.size()) {
Node t = q.top();
q.pop();
int S = t.id;
ans[cnt++] = S;
for (int i = h[S]; ~i; i = ne[i]) {
int j = e[i];
if( -- din[j] == 0)
q.push(job[j]);
}
}
}
int main() {
while(~scanf("%d", &n)) {
init();
for (int i = 1; i <= n; i ++) {
scanf("%d%d", &job[i].t, &job[i].e);
}
int m;
scanf("%d", &m);
while(m --) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
din[b]++;
}
topsort();
for (int i = 0; i < n; i ++) {
printf("%d\n", ans[i]);
}
}
return 0;
}