舞蹈链算法详解
ZOJ 3209 题目链接
题意
给定P个完整矩形的小矩形,找到最少的可以填充原来完整矩阵的矩形数
思路
DLX(精准覆盖)算法,把p个矩形当成行,把每一小格当成列(有nm小格),然后填充精确填充nm列即可
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 505;
const int M = 905;
struct node{
int n,m,id;
int U[N * M],D[N * M],R[N * M],L[N * M],Row[N * M],Col[N * M];
int H[N];/// 行头结点
int S[M];
int ansd,ans[N];
void init(int _n,int _m){
n = _n;
m = _m;
for (int i = 0; i <= m; i++){
S[i] = 0;
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
L[0] = m;
R[m] = 0;
id = m;
for (int i = 1; i <= n; i++){
H[i] = -1;
}
}
void link(int r,int c){
Col[++id] = c;
S[c]++;
Row[id] = r;
D[id] = D[c];
U[D[c]] = id;
U[id] = c;
D[c] = id;
if (H[r] < 0){
H[r] = L[id] = R[id] = id;
}
else {
R[id] = R[H[r]];
L[R[H[r]]] = id;
L[id] = H[r];
R[H[r]] = id;
}
}
void Remove(int c){
L[R[c]] = L[c];
R[L[c]] = R[c];
for (int i = D[c]; i != c; i = D[i]){
for (int j = R[i]; j != i; j = R[j]){
U[D[j]] = U[j];
D[U[j]] = D[j];
S[Col[j]]--;
}
}
}
void Resume(int c){
for (int i = U[c]; i != c; i = U[i]){
for (int j = L[i]; j != i; j = L[j]){
U[D[j]] = D[U[j]] = j;
S[Col[j]]++;
}
}
L[R[c]] = R[L[c]] = c;
}
void dance(int deep){
if (ansd != -1 && ansd <= deep) return ;
if (R[0] == 0){
if (ansd == -1) ansd = deep;
ansd = min(ansd,deep);
return ;
}
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]){
if (S[i] < S[c]) {
c = i;
}
}
Remove(c);
for (int i = D[c]; i != c; i = D[i]){
ans[deep] = Row[i];
for (int j = R[i]; j != i; j = R[j]){
Remove(Col[j]);
}
dance(deep + 1);
for (int j = L[i]; j != i; j = L[j]){
Resume(Col[j]);
}
}
Resume(c);
}
}DLX;
int pos[33][33];
void get_pos(int n,int m)
{
int cnt = 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
pos[i][j] = cnt++;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m,p;
scanf("%d %d %d",&n,&m,&p);
DLX.init(p,n * m);
get_pos(n,m);
for (int r = 1; r <= p; r++){
int x1,y1,x3,y3;
scanf("%d %d %d %d",&x1,&y1,&x3,&y3);
for (int i = x1 + 1; i <= x3; i++){
for (int j = y1 + 1; j <= y3; j++){
DLX.link(r,pos[i][j]);
}
}
}
DLX.ansd = -1;
DLX.dance(0);
printf("%d\n",DLX.ansd);
}
return 0;
}
HDU 2295 题目链接
题意
n个城市,m个雷达,雷达有限制,问雷达的最小半径是多少可以覆盖所有城市
思路
雷达为行,城市为列,DLX重复覆盖,二分查找半径跑一遍DLX
DLX中的calc类似A*算法的评估函数
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
const double eps = 1e-8;
const int N = 66;
const int M = 66;
int limit;
struct node{
int n,m,id;
int U[N * M],D[N * M],R[N * M],L[N * M],Row[N * M],Col[N * M];
int H[N];/// 行头结点
int S[M];
bool v[N * M];
int ansd,ans[N];
void init(int _n,int _m)
{
n = _n;
m = _m;
for (int i = 0; i <= m; i++){
S[i] = 0;
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
L[0] = m;
R[m] = 0;
id = m;
for (int i = 1; i <= n; i++){
H[i] = -1;
}
}
void link(int r,int c)
{
Col[++id] = c;
S[c]++;
Row[id] = r;
D[id] = D[c];
U[D[c]] = id;
U[id] = c;
D[c] = id;
if (H[r] < 0){
H[r] = L[id] = R[id] = id;
}
else {
R[id] = R[H[r]];
L[R[H[r]]] = id;
L[id] = H[r];
R[H[r]] = id;
}
}
void Remove(int c)
{
for (int i = D[c]; i != c; i = D[i]){
L[R[i]] = L[i];
R[L[i]] = R[i];
}
}
void Resume(int c)
{
for (int i = U[c]; i != c; i = U[i]){
L[R[i]] = R[L[i]] = i;
}
}
int calc()
{
int cnt = 0;
for (int i = R[0]; i != 0; i = R[i]){
v[i] = true;
}
for (int i = R[0]; i != 0; i = R[i]){
if (v[i]){
cnt++;
v[i] = false;
for (int j = D[i]; j != i; j = D[j]){
for (int k = R[j]; k != j; k = R[k]){
v[Col[k]] = false;
}
}
}
}
return cnt;
}
bool dance(int deep)
{
if (deep + calc() > limit) return false;
if (deep > limit) return false;
if (R[0] == 0) return true;
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]){
if (S[i] < S[c]){
c = i;
}
}
for (int i = D[c]; i != c; i = D[i]){
Remove(i);
for (int j = R[i]; j != i; j = R[j]){
Remove(j);
}
if (dance(deep + 1)) return true;
for (int j = L[i]; j != i; j = L[j]){
Resume(j);
}
Resume(i);
}
return false;
}
}DLX;
struct Point{
double x,y;
}city[55],radar[55];
double dist(int i,int j)
{
return sqrt((city[i].x - radar[j].x) * (city[i].x - radar[j].x) + (city[i].y - radar[j].y) * (city[i].y - radar[j].y));
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d %d %d",&n,&m,&limit);
for (int i = 1; i <= n; i++){
scanf("%lf %lf",&city[i].x,&city[i].y);
}
for (int j = 1; j <= m; j++){
scanf("%lf %lf",&radar[j].x,&radar[j].y);
}
double l = 0,r = 1e5;
while(r - l >= eps){
double mid = (l + r) / 2.0;
DLX.init(m,n);
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
if (dist(i,j) <= mid){
DLX.link(j,i);
}
}
}
if (DLX.dance(0)) r = mid;
else l = mid;
}
printf("%.6f\n",r);
}
return 0;
}
POJ 1084 题目链接
题意
有很多个火柴形成的正方形,有m个火柴已被破坏,问还需要破坏多少个火柴才能破坏所有的正方形
思路
把正方形当成列,火柴当成行,把所有的正方形破坏,就相当于选择所有的列
DLX重复覆盖
预处理所有的火柴和正方形的行列关系,然后标记m个火柴,把这些已经破坏的正方形列去掉
(也可以写个remove1函数(不会写,照抄别人的),在添加所有的结点后,再把m个火柴破坏的列去掉)
void remove1(int r)
{
if(H[r]==-1)
return;
for(int i=U[H[r]];i!=H[r];i=U[i])
{
if(H[row[i]]==i) // !!!
{
if(R[i]==i)
H[row[i]]=-1;
else
H[row[i]]=R[i];
}
L[R[i]]=L[i];
R[L[i]]=R[i];
}
for(int i=R[H[r]];i!=H[r];i=R[i])
for(int j=U[i];j!=i;j=U[j])
{
if(H[row[j]]==j)
{
if(R[j]==j)
H[row[j]]=-1;
else
H[row[j]]=R[j];
}
L[R[j]]=L[j];
R[L[j]]=R[j];
}
}
#include <cstdio>
#include <cmath>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int N = 222;
const int M = 222;
struct node{
int n,m,id;
int U[N * M],D[N * M],R[N * M],L[N * M],Row[N * M],Col[N * M];
int H[N];/// 行头结点
int S[M];
bool v[N * M];
int ansd,ans[N];
void init(int _n,int _m)
{
n = _n;
m = _m;
for (int i = 0; i <= m; i++){
S[i] = 0;
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
L[0] = m;
R[m] = 0;
id = m;
for (int i = 1; i <= n; i++){
H[i] = -1;
}
}
void link(int r,int c)
{
Col[++id] = c;
S[c]++;
Row[id] = r;
D[id] = D[c];
U[D[c]] = id;
U[id] = c;
D[c] = id;
if (H[r] < 0){
H[r] = L[id] = R[id] = id;
}
else {
R[id] = R[H[r]];
L[R[H[r]]] = id;
L[id] = H[r];
R[H[r]] = id;
}
}
void Remove(int c)
{
for (int i = D[c]; i != c; i = D[i]){
L[R[i]] = L[i];
R[L[i]] = R[i];
}
}
void Resume(int c)
{
for (int i = U[c]; i != c; i = U[i]){
L[R[i]] = R[L[i]] = i;
}
}
int calc()
{
int cnt = 0;
for (int i = R[0]; i != 0; i = R[i]){
v[i] = true;
}
for (int i = R[0]; i != 0; i = R[i]){
if (v[i]){
cnt++;
v[i] = false;
for (int j = D[i]; j != i; j = D[j]){
for (int k = R[j]; k != j; k = R[k]){
v[Col[k]] = false;
}
}
}
}
return cnt;
}
void dance(int deep)
{
if (deep + calc() >= ansd) return ;
if (R[0] == 0) {
ansd = min(ansd,deep);
return ;
}
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]){
if (S[i] < S[c]){
c = i;
}
}
for (int i = D[c]; i != c; i = D[i]){
Remove(i);
for (int j = R[i]; j != i; j = R[j]){
Remove(j);
}
dance(deep + 1);
for (int j = L[i]; j != i; j = L[j]){
Resume(j);
}
Resume(i);
}
}
}DLX;
int id[66][66];
int num1[6] = {0,4,12,24,40,60};
int num2[6] = {0,1,5,14,30,55};
bool vis[66];
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d %d",&n,&m);
int gap = 2 * n + 1;
int cnt = 0;
memset(id,0,sizeof(id));
for (int len = 1; len <= n; len++){ /// 正方形边长
for (int i = 1; i <= (n - len) * gap + 1; i += gap){ /// 正方形的一层中的第一个火柴编号
for (int j = i; j - i <= n - len; j++){ /// 遍历正方形的一层
++cnt;
for (int k = j; k - j + 1 <= len; k++){ /// 把正方形的上下火柴标记
id[k][cnt] = 1;
id[k + len * gap][cnt] = 1;
// cout << "cnt = " << cnt << " " << k << " " << k + len * gap << endl;
}
for (int k = j + n; k <= j + n + gap * (len - 1); k += gap){ /// 把正方形的左右火柴标记
id[k][cnt] = 1;
id[k + len][cnt] = 1;
// cout << "cnt = " << cnt << " " << k << " " << k + len << endl;
}
}
}
}
int sum = 2 * n * (n + 1);
DLX.init(sum,cnt);
memset(vis,true,sizeof(vis));
for (int i = 0; i < m; i++){
int x;
scanf("%d",&x);
for (int j = 1; j <= num2[n]; j++){
if (id[x][j] && vis[j]){
vis[j] = false;
DLX.R[DLX.L[j]] = DLX.R[j];
DLX.L[DLX.R[j]] = DLX.L[j];
DLX.R[j] = DLX.L[j] = 0;
}
}
}
for (int i = 1; i <= num1[n]; i++){
for (int j = 1; j <= num2[n]; j++){
if (id[i][j] && vis[j]) {
DLX.link(i,j);
}
}
}
DLX.ansd = INF;
DLX.dance(0);
printf("%d\n",DLX.ansd);
}
return 0;
}
POJ 3074 题目链接
题意
9个3*3的数独
思路
转化成DLX精准覆盖问题
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1111 * 4;
const int M = 1111;
struct node{
int n,m,id;
int U[N * M],D[N * M],R[N * M],L[N * M],Row[N * M],Col[N * M];
int H[N];/// 行头结点
int S[M];
int ansd,ans[N];
void init(int _n,int _m){
n = _n;
m = _m;
for (int i = 0; i <= m; i++){
S[i] = 0;
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
L[0] = m;
R[m] = 0;
id = m;
for (int i = 1; i <= n; i++){
H[i] = -1;
}
}
void link(int r,int c){
Col[++id] = c;
S[c]++;
Row[id] = r;
D[id] = D[c];
U[D[c]] = id;
U[id] = c;
D[c] = id;
if (H[r] < 0){
H[r] = L[id] = R[id] = id;
}
else {
R[id] = R[H[r]];
L[R[H[r]]] = id;
L[id] = H[r];
R[H[r]] = id;
}
}
void Remove(int c){
L[R[c]] = L[c];
R[L[c]] = R[c];
for (int i = D[c]; i != c; i = D[i]){
for (int j = R[i]; j != i; j = R[j]){
U[D[j]] = U[j];
D[U[j]] = D[j];
S[Col[j]]--;
}
}
}
void Resume(int c){
for (int i = U[c]; i != c; i = U[i]){
for (int j = L[i]; j != i; j = L[j]){
U[D[j]] = D[U[j]] = j;
S[Col[j]]++;
}
}
L[R[c]] = R[L[c]] = c;
}
bool dance(int deep)
{
if (R[0] == 0){
ansd = deep;
return true;
}
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]){
if (S[i] < S[c]) {
c = i;
}
}
Remove(c);
for (int i = D[c]; i != c; i = D[i]){
ans[deep] = Row[i];
for (int j = R[i]; j != i; j = R[j]){
Remove(Col[j]);
}
if (dance(deep + 1)) return true;
for (int j = L[i]; j != i; j = L[j]){
Resume(Col[j]);
}
}
Resume(c);
return false;
}
}DLX;
char str[99];
int main()
{
while(scanf("%s",str) != EOF && str[0] != 'e'){
DLX.init(81 * 9,81 * 4);
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
int x = i, y = j,z = i / 3 * 3 + j / 3;
int id = i * 9 + j;
if (str[id] == '.'){
for (int k = 1; k <= 9; k++){ /// 每一个位置有九种情况(连接对应的数字和行列方格)
DLX.link(id * 9 + k,id + 1); /// 数字
DLX.link(id * 9 + k,81 + x * 9 + k); /// 行
DLX.link(id * 9 + k,162 + y * 9 + k); /// 列
DLX.link(id * 9 + k,243 + z * 9 + k); /// 小方格
}
}
else {
int k = str[id] - '0';
DLX.link(id * 9 + k,id + 1); /// 数字
DLX.link(id * 9 + k,81 + x * 9 + k); /// 行
DLX.link(id * 9 + k,162 + y * 9 + k); /// 列
DLX.link(id * 9 + k,243 + z * 9 + k); /// 小方格
}
}
}
DLX.dance(0);
for (int i = 0; i < DLX.ansd; i++){
int k = DLX.ans[i];
int a = (k - 1) / 9;
int b = (k - 1) % 9 + '1';
str[a / 9 * 9 + a % 9] = b;
}
puts(str);
}
return 0;
}
HDU 4069 题目链接
题意
变形版数独,之前的3*3小格变成的连通块
输入的数字为五个数的和,代表那一个块的上下左右是否有墙
思路
先跑一个dfs把每一个块标记出来
注意第一次找到的时候保存结果(因为后面寻找会改变结果)
找到第二个就立刻退出,不然会超时
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10;
const int NM = N * N * N * 4 + N * N * 4 + N;
const int M = N * N * N + N;
struct node{
int n,m,id;
int U[NM],D[NM],R[NM],L[NM],Row[NM],Col[NM];
int H[NM];/// 行头结点
int S[NM];
int ansd,ans[M],cnt,real[N * N];
void init(int _n,int _m){
n = _n;
m = _m;
for (int i = 0; i <= m; i++){
S[i] = 0;
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
L[0] = m;
R[m] = 0;
id = m;
cnt = 0;
for (int i = 1; i <= n; i++){
H[i] = -1;
}
}
void link(int r,int c){
Col[++id] = c;
S[c]++;
Row[id] = r;
D[id] = D[c];
U[D[c]] = id;
U[id] = c;
D[c] = id;
if (H[r] < 0){
H[r] = L[id] = R[id] = id;
}
else {
R[id] = R[H[r]];
L[R[H[r]]] = id;
L[id] = H[r];
R[H[r]] = id;
}
}
void Remove(int c){
L[R[c]] = L[c];
R[L[c]] = R[c];
for (int i = D[c]; i != c; i = D[i]){
for (int j = R[i]; j != i; j = R[j]){
U[D[j]] = U[j];
D[U[j]] = D[j];
S[Col[j]]--;
}
}
}
void Resume(int c){
for (int i = U[c]; i != c; i = U[i]){
for (int j = L[i]; j != i; j = L[j]){
U[D[j]] = D[U[j]] = j;
S[Col[j]]++;
}
}
L[R[c]] = R[L[c]] = c;
}
void dance(int deep)
{
if (R[0] == 0){
cnt++;
if (cnt > 1) return ;
ansd = deep;
for (int i = 0; i < deep; i++){
real[(ans[i] - 1) / 9] = (ans[i] - 1) % 9 + 1;
}
return ;
}
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]){
if (S[i] < S[c]) {
c = i;
}
}
Remove(c);
for (int i = D[c]; i != c; i = D[i]){
ans[deep] = Row[i];
for (int j = R[i]; j != i; j = R[j]){
Remove(Col[j]);
}
dance(deep + 1);
if (cnt > 1) return ;
for (int j = L[i]; j != i; j = L[j]){
Resume(Col[j]);
}
}
Resume(c);
}
}DLX;
int g[N][N];
int id[N][N];
int hinder[N][N][4];
int to[4][2] = {0,-1,1,0,0,1,-1,0};
int num;
void dfs(int x,int y)
{
id[x][y] = num;
for (int i = 0; i < 4; i++){
int xx = x + to[i][0];
int yy = y + to[i][1];
if (id[xx][yy] || hinder[x][y][i]) continue;
dfs(xx,yy);
}
}
int main()
{
int t,Case = 1;
scanf("%d",&t);
while(t--){
memset(hinder,0,sizeof(hinder));
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
scanf("%d",&g[i][j]);
if (g[i][j] >= 128) {
hinder[i][j][0] = 1;
g[i][j] -= 128;
}
if (g[i][j] >= 64){
hinder[i][j][1] = 1;
g[i][j] -= 64;
}
if (g[i][j] >= 32) {
hinder[i][j][2] = 1;
g[i][j] -= 32;
}
if (g[i][j] >= 16){
hinder[i][j][3] = 1;
g[i][j] -= 16;
}
}
}
num = 1;
memset(id,0,sizeof(id));
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
if (!id[i][j]) {
dfs(i,j);
num++;
}
}
}
DLX.init(81 * 9,81 * 4);
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
int x = i,y = j,z = id[i][j] - 1;
int tem = i * 9 + j;
if (!g[i][j]){
for (int k = 1; k <= 9; k++){
DLX.link(tem * 9 + k,tem + 1);
DLX.link(tem * 9 + k,81 + x * 9 + k);
DLX.link(tem * 9 + k,162 + y * 9 + k);
DLX.link(tem * 9 + k,243 + z * 9 + k);
}
}
else {
int k = g[i][j];
DLX.link(tem * 9 + k,tem + 1);
DLX.link(tem * 9 + k,81 + x * 9 + k);
DLX.link(tem * 9 + k,162 + y * 9 + k);
DLX.link(tem * 9 + k,243 + z * 9 + k);
}
}
}
DLX.dance(0);
printf("Case %d:\n",Case++);
if (DLX.cnt == 0) printf("No solution\n");
else if (DLX.cnt > 1) printf("Multiple Solutions\n");
else {
for (int i = 0; i < DLX.ansd; i++){
printf("%d",DLX.real[i]);
if (i % 9 == 8) printf("\n");
}
}
}
return 0;
}
HDU 3335 题目链接
题意
给出n个数,找出最多的两两不能相除的数
思路
DLX最大可重复覆盖,num[i]与num[j]可以相除就在i行j列和j行i列标1
(我也不知道为什么最大可重复覆盖可以解)
(这道题用二分图匹配也可以解,但是建图的时候是有向图)
for (int i = 0; i < n; i++){
for (int j = 0; j < i; j++){
if (a[i] % a[j] == 0 || a[j] % a[i] == 0){
g[i][j] = true;
}
}
}
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
const double eps = 1e-8;
const int NM = 1000010;
const int N = 1111;
const int M = 1111;
struct node{
int n,m,id;
int U[NM],D[NM],R[NM],L[NM],Row[NM],Col[NM];
int H[N];/// 行头结点
int S[N];
bool v[NM];
int ansd,ans[N];
void init(int _n,int _m)
{
n = _n;
m = _m;
for (int i = 0; i <= m; i++){
S[i] = 0;
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
L[0] = m;
R[m] = 0;
id = m;
for (int i = 1; i <= n; i++){
H[i] = -1;
}
}
void link(int r,int c)
{
Col[++id] = c;
S[c]++;
Row[id] = r;
D[id] = D[c];
U[D[c]] = id;
U[id] = c;
D[c] = id;
if (H[r] < 0){
H[r] = L[id] = R[id] = id;
}
else {
R[id] = R[H[r]];
L[R[H[r]]] = id;
L[id] = H[r];
R[H[r]] = id;
}
}
void Remove(int c)
{
for (int i = D[c]; i != c; i = D[i]){
L[R[i]] = L[i];
R[L[i]] = R[i];
}
}
void Resume(int c)
{
for (int i = U[c]; i != c; i = U[i]){
L[R[i]] = R[L[i]] = i;
}
}
int calc()
{
int cnt = 0;
for (int i = R[0]; i != 0; i = R[i]){
v[i] = true;
}
for (int i = R[0]; i != 0; i = R[i]){
if (v[i]){
cnt++;
v[i] = false;
for (int j = D[i]; j != i; j = D[j]){
for (int k = R[j]; k != j; k = R[k]){
v[Col[k]] = false;
}
}
}
}
return cnt;
}
void dance(int deep)
{
if (deep + calc() <= ansd) return ;
if (R[0] == 0) {
ansd = deep;
return ;
}
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]){
if (S[i] < S[c]){
c = i;
}
}
for (int i = D[c]; i != c; i = D[i]){
Remove(i);
for (int j = R[i]; j != i; j = R[j]){
Remove(j);
}
dance(deep + 1);
for (int j = L[i]; j != i; j = L[j]){
Resume(j);
}
Resume(i);
}
}
}DLX;
long long a[N];
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
DLX.init(n,n);
for (int i = 1; i <= n; i++){
scanf("%I64d",&a[i]);
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= i; j++){
if ((a[i] % a[j] == 0) || (a[j] % a[i] == 0)){
DLX.link(i,j);
DLX.link(j,i);
}
}
}
DLX.ansd = 0;
DLX.dance(0);
printf("%d\n",DLX.ansd);
}
return 0;
}
HDU 4979 题目链接
题意
n选m彩票中,至少买几张彩票保证至少能中r个数.
思路
使用二进制代表选择的号码
C(n,m)当作行,C(n,r)当作列
要打表,8,5,4这个有点慢
///打表主函数
int main()
{
int t,Case = 1;
scanf("%d",&t);
while(t--){
int n,m,r;
// scanf("%d %d %d",&n,&m,&r);
for (int i = 1; i <= 8; i++){
for (int j = 1; j <= i; j++){
for (int k = 1; k <= j; k++){
n = i;
m = j;
r = k;
if (n == 8 && m == 5 && r == 4) continue;
int sum1 = fac(n) / fac(m) / fac(n - m);
int sum2 = fac(n) / fac(r) / fac(n - r);
DLX.init(sum1,sum2);
int cnt1 = 0,cnt2 = 0;
for (int i = 1; i < pow(2,n); i++){
if (calc(i,n) == m) num1[++cnt1] = i;
if (calc(i,n) == r) num2[++cnt2] = i;
}
for (int i = 1; i <= cnt1; i++){
for (int j = 1; j <= cnt2; j++){
if ((num1[i] & num2[j]) == num2[j]){
DLX.link(i,j);
}
}
}
DLX.ansd = INF;
DLX.dance(0);
printf("n = %d m = %d r = %d 总数为:",n,m,r);
printf("%d\n",DLX.ansd);
}
}
}
}
return 0;
}
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int NM = 1000010;
const int N = 1111;
const int M = 1111;
struct node{
int n,m,id;
int U[NM],D[NM],R[NM],L[NM],Row[NM],Col[NM];
int H[N];/// 行头结点
int S[N];
bool v[NM];
int ansd,ans[N];
void init(int _n,int _m)
{
n = _n;
m = _m;
for (int i = 0; i <= m; i++){
S[i] = 0;
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
L[0] = m;
R[m] = 0;
id = m;
for (int i = 1; i <= n; i++){
H[i] = -1;
}
}
void link(int r,int c)
{
Col[++id] = c;
S[c]++;
Row[id] = r;
D[id] = D[c];
U[D[c]] = id;
U[id] = c;
D[c] = id;
if (H[r] < 0){
H[r] = L[id] = R[id] = id;
}
else {
R[id] = R[H[r]];
L[R[H[r]]] = id;
L[id] = H[r];
R[H[r]] = id;
}
}
void Remove(int c)
{
for (int i = D[c]; i != c; i = D[i]){
L[R[i]] = L[i];
R[L[i]] = R[i];
}
}
void Resume(int c)
{
for (int i = U[c]; i != c; i = U[i]){
L[R[i]] = R[L[i]] = i;
}
}
int calc()
{
int cnt = 0;
for (int i = R[0]; i != 0; i = R[i]){
v[i] = true;
}
for (int i = R[0]; i != 0; i = R[i]){
if (v[i]){
cnt++;
v[i] = false;
for (int j = D[i]; j != i; j = D[j]){
for (int k = R[j]; k != j; k = R[k]){
v[Col[k]] = false;
}
}
}
}
return cnt;
}
void dance(int deep)
{
if (deep + calc() >= ansd) return ;
if (R[0] == 0) {
ansd = min(ansd,deep);
return ;
}
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]){
if (S[i] < S[c]){
c = i;
}
}
for (int i = D[c]; i != c; i = D[i]){
Remove(i);
for (int j = R[i]; j != i; j = R[j]){
Remove(j);
}
dance(deep + 1);
for (int j = L[i]; j != i; j = L[j]){
Resume(j);
}
Resume(i);
}
}
}DLX;
int fac(int n)
{
int ans = 1;
for (int i = 1; i <= n; i++){
ans *= i;
}
return ans;
}
int calc(int x,int n)
{
int ans = 0;
for (int i = 0; i < n; i++){
if ((1 << i) & x) ans++;
}
return ans;
}
int num1[111];
int num2[111];
int ans[]{
0,8,4,28,3,11,56,2,6,14,70,2,4,8,20,56,2,3,4,7,12,28,
2,3,4,5,6,7,8,1,1,1,1,1,1,1,1
};
int main()
{
int t,Case = 1;
scanf("%d",&t);
while(t--){
int n,m,r;
scanf("%d %d %d",&n,&m,&r);
if (n == 8){
int num = 1,res;
bool flag = false;
for (int i = 1; i <= 8; i++){
for (int j = 1; j <= i; j++){
if (m == i && r == j){
res = ans[num];
flag = true;
}
if (flag) break;
num++;
}
}
printf("Case #%d: %d\n",Case++,res);
continue;
}
int sum1 = fac(n) / fac(m) / fac(n - m);
int sum2 = fac(n) / fac(r) / fac(n - r);
DLX.init(sum1,sum2);
int cnt1 = 0,cnt2 = 0;
for (int i = 1; i < pow(2,n); i++){
if (calc(i,n) == m) num1[++cnt1] = i;
if (calc(i,n) == r) num2[++cnt2] = i;
}
for (int i = 1; i <= cnt1; i++){
for (int j = 1; j <= cnt2; j++){
if ((num1[i] & num2[j]) == num2[j]){
DLX.link(i,j);
}
}
}
DLX.ansd = INF;
DLX.dance(0);
printf("Case #%d: %d\n",Case++,DLX.ansd);
}
return 0;
}
HDU 5046 题目链接
题意
n个城市,建立k个机场,求所有城市到机场的最小距离
思路
二分,类似HDU2295,注意long long
#include <cstdio>
#include <cmath>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int NM = 1000010;
const int N = 1111;
const int M = 1111;
int limit;
struct dlx{
int n,m,id;
int U[NM],D[NM],R[NM],L[NM],Row[NM],Col[NM];
int H[N];/// 行头结点
int S[N];
bool v[NM];
int ansd,ans[N];
void init(int _n,int _m)
{
n = _n;
m = _m;
for (int i = 0; i <= m; i++){
S[i] = 0;
U[i] = D[i] = i;
L[i] = i - 1;
R[i] = i + 1;
}
L[0] = m;
R[m] = 0;
id = m;
for (int i = 1; i <= n; i++){
H[i] = -1;
}
}
void link(int r,int c)
{
Col[++id] = c;
S[c]++;
Row[id] = r;
D[id] = D[c];
U[D[c]] = id;
U[id] = c;
D[c] = id;
if (H[r] < 0){
H[r] = L[id] = R[id] = id;
}
else {
R[id] = R[H[r]];
L[R[H[r]]] = id;
L[id] = H[r];
R[H[r]] = id;
}
}
void Remove(int c)
{
for (int i = D[c]; i != c; i = D[i]){
L[R[i]] = L[i];
R[L[i]] = R[i];
}
}
void Resume(int c)
{
for (int i = U[c]; i != c; i = U[i]){
L[R[i]] = R[L[i]] = i;
}
}
int calc()
{
int cnt = 0;
for (int i = R[0]; i != 0; i = R[i]){
v[i] = true;
}
for (int i = R[0]; i != 0; i = R[i]){
if (v[i]){
cnt++;
v[i] = false;
for (int j = D[i]; j != i; j = D[j]){
for (int k = R[j]; k != j; k = R[k]){
v[Col[k]] = false;
}
}
}
}
return cnt;
}
bool dance(int deep)
{
if (deep + calc() > limit) return false;
if (R[0] == 0) {
return deep <= limit;
}
int c = R[0];
for (int i = R[0]; i != 0; i = R[i]){
if (S[i] < S[c]){
c = i;
}
}
for (int i = D[c]; i != c; i = D[i]){
Remove(i);
for (int j = R[i]; j != i; j = R[j]){
Remove(j);
}
if (dance(deep + 1)) return true;
for (int j = L[i]; j != i; j = L[j]){
Resume(j);
}
Resume(i);
}
return false;
}
}DLX;
struct node{
ll x,y;
}s[N];
ll dis(int i,int j)
{
return (ll)abs(s[i].x - s[j].x) + (ll)abs(s[i].y - s[j].y);
}
int main()
{
int t,Case = 1;
scanf("%d",&t);
while(t--){
int n;
scanf("%d %d",&n,&limit);
for (int i = 1; i <= n; i++){
scanf("%I64d %I64d",&s[i].x,&s[i].y);
}
ll l = 0,r = 100000000000,ans;
while(l <= r){
ll mid = (l + r) >> 1;
DLX.init(n,n);
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
if (dis(i,j) <= mid){
DLX.link(i,j);
}
}
}
if (DLX.dance(0)) {
ans = mid;
r = mid - 1;
}
else l = mid + 1;
}
printf("Case #%d: %I64d\n",Case++,ans);
}
return 0;
}