HDU 3957 Street Fighter (最小支配集 DLX 重复覆盖+精确覆盖 )
DLX经典题型,被虐惨了……
建一个2*N行3*N列的矩阵,行代表选择,列代表约束。前2*N列代表每个人的哪种状态,后N列保证每个人至多选一次。
显然对手可以被战胜多次(重复覆盖),每个角色至多选择一次(精确覆盖)。
注意事项:
1.行数=∑每个人的模式数,之前我直接把行数当2*N了……但实际上也会有人只有一种模式的,也就是说实际行数小于等于2*N
2.建图的时候注意:这个人不光能覆盖他所战胜的某角色的某模式,还覆盖了他自己的所有模式(因为他不用战胜自己)。之前没注意这个问题,样例全成无解了orz……
3.处理精确覆盖和重复覆盖的先后顺序。如果优先处理精确覆盖,会把重复覆盖的一些行也删掉,这样前面可以重复覆盖的很多列也被当成了精确覆盖,显然不对了。所以应当先处理重复覆盖。恢复的时候遵循先删除的后恢复,后删除的先恢复。
4.只要满足重复覆盖的条件即为一个可行解。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm> using namespace std; const int MAXN = ;
const int INF = << ; int N;
int U[ (*MAXN)*(*MAXN) ], D[ (*MAXN)*(*MAXN) ];
int L[ (*MAXN)*(*MAXN) ], R[ (*MAXN)*(*MAXN) ];
int C[ (*MAXN)*(*MAXN) ];
int cnt[ *MAXN ];
bool mx[ *MAXN ][ *MAXN ];
bool vis[ *MAXN ];
bool vs[MAXN][][MAXN][]; //i的x模式能打败j的y模式
int modelN[MAXN]; //i有几个模式
int sum[MAXN];
int head;
int maxr, maxc; void Remove( int c ) //重复覆盖删除列
{
for ( int i = D[c]; i != c; i = D[i] )
{
R[ L[i] ] = R[i];
L[ R[i] ] = L[i];
}
return;
} void Resume( int c ) //重复覆盖恢复列
{
for ( int i = D[c]; i != c; i = D[i] )
{
R[ L[i] ] = i;
L[ R[i] ] = i;
}
return;
} void ExRemove( int c ) //精确覆盖删除列+行
{
int i, j;
L[ R[c] ] = L[c];
R[ L[c] ] = R[c];
for ( i = D[c]; i != c; i = D[i] )
{
for ( j = R[i]; j != i; j = R[j] )
{
U[ D[j] ] = U[j];
D[ U[j] ] = D[j];
--cnt[ C[j] ];
}
}
return;
} void ExResume( int c ) //精确覆盖恢复列+行
{
int i, j;
R[ L[c] ] = c;
L[ R[c] ] = c;
for ( i = D[c]; i != c; i = D[i] )
{
for ( j = R[i]; j != i; j = R[j] )
{
U[ D[j] ] = j;
D[ U[j] ] = j;
++cnt[ C[j] ];
}
}
return;
} bool build()
{
head = ;
for ( int i = ; i < maxc; ++i )
{
R[i] = i + ;
L[i + ] = i;
}
R[maxc] = ;
L[] = maxc; //列链表
for ( int j = ; j <= maxc; ++j )
{
int pre = j;
cnt[j] = ;
for ( int i = ; i <= maxr; ++i )
{
if ( mx[i][j] )
{
++cnt[j];
int cur = i * maxc + j;
U[cur] = pre;
D[pre] = cur;
C[cur] = j;
pre = cur;
}
}
U[j] = pre;
D[pre] = j;
//if ( !cnt[j] ) return false;
} //行链表
for ( int i = ; i <= maxr; ++i )
{
int pre = -, first = -;
for ( int j = ; j <= maxc; ++j )
{
if ( mx[i][j] )
{
int cur = i * maxc + j;
if ( pre == - ) first = cur;
else
{
L[cur] = pre;
R[pre] = cur;
}
pre = cur;
}
}
if ( first != - )
{
R[pre] = first;
L[first] = pre;
}
} return true;
} /****************以上DLX模板****************/ //估价函数:至少还要选几个人
int h()
{
memset( vis, false, sizeof(vis) );
int res = ;
for ( int c = R[head]; c <= maxr && c != head; c = R[c] )
{
if ( !vis[c] )
{
++res;
vis[c] = true;
for ( int i = D[c]; i != c; i = D[i] )
for ( int j = R[i]; j != i; j = R[j] )
vis[ C[j] ] = true;
}
}
return res;
} bool DFS( int dep, int limit )
{
//A-star剪枝
if ( dep + h() > limit ) return false; //只要前面满足重复覆盖的条件,即为可行解
if ( R[head] > maxr || R[head] == head ) return true; int c, minv = INF;
for ( int i = R[head]; i <= maxr && i != head; i = R[i] )
{
if ( cnt[i] < minv )
{
minv = cnt[i];
c = i;
}
} for ( int i = D[c]; i != c; i = D[i] )
{
Remove(i);
//注意处理重复覆盖和精确覆盖的顺序
for ( int j = R[i]; j != i; j = R[j] )
if ( C[j] <= maxr ) Remove(j); for ( int j = R[i]; j != i; j = R[j] )
if ( C[j] > maxr ) ExRemove( C[j] ); if ( DFS( dep + , limit ) )
{
//注意恢复精确覆盖和重复覆盖的顺序,这样恢复之后可以不必重新建图
for ( int j = R[i]; j != i; j = R[j] )
if ( C[j] > maxr ) ExResume( C[j] ); for ( int j = R[i]; j != i; j = R[j] )
if ( C[j] <= maxr ) Resume(j); Resume(i); //之前忘了恢复i,死活TLE
return true;
} for ( int j = R[i]; j != i; j = R[j] )
if ( C[j] > maxr ) ExResume( C[j] );
for ( int j = R[i]; j != i; j = R[j] )
if ( C[j] <= maxr ) Resume(j);
Resume(i);
} return false;
} int solved()
{
int l = , r = N;
int ans; while ( l <= r )
{
int mid = ( l + r ) >> ;
if ( DFS( , mid ) )
{
r = mid - ;
ans = mid;
}
else l = mid + ;
} return ans;
} void show()
{
for ( int i = ; i <= maxr; ++i )
{
for ( int j = ; j <= maxc; ++j )
printf( "%d", mx[i][j] );
puts("");
}
} void init()
{
memset( mx, false, sizeof(mx) ); for( int i = ; i < N; ++i )
{
for ( int x = ; x < modelN[i]; ++x )
{
mx[ sum[i] + x ][ maxr + i + ] = true;
mx[ sum[i] + x ][ sum[i] ] = true;
if ( modelN[i] > )
{
mx[ sum[i] + x ][ sum[i] + ] = true;
}
for ( int j = ; j < N; ++j )
{
for ( int y = ; y < modelN[j]; ++y )
{
if ( vs[i][x][j][y] )
{
mx[ sum[i] + x ][ sum[j] + y ] = true;
}
}
}
}
} //show();
return;
} int main()
{
//freopen( "in.txt", "r", stdin );
//freopen( "out.txt", "w", stdout );
int T, cas = ;
scanf( "%d", &T );
while ( T-- )
{
memset( vs, false, sizeof(vs) );
scanf( "%d", &N );
maxr = ; for ( int i = ; i < N; ++i )
{
scanf( "%d", &modelN[i] );
sum[i] = maxr + ;
for ( int j = ; j < modelN[i]; ++j )
{
++maxr;
int K;
scanf( "%d", &K );
for ( int k = ; k < K; ++k )
{
int id, mode;
scanf( "%d%d", &id, &mode );
vs[i][j][id][mode] = true;
}
}
} maxc = maxr + N;
init();
build();
printf("Case %d: %d\n", ++cas, solved() );
}
return ;
}
HDU 3957 Street Fighter (最小支配集 DLX 重复覆盖+精确覆盖 )的更多相关文章
- HDU 3957 Street Fighter(搜索、DLX、重复覆盖+精确覆盖)
很久以前就看到的一个经典题,一直没做,今天拿来练手.街霸 给n<=25个角色,每个角色有 1 or 2 个版本(可以理解为普通版以及爆发版),每个角色版本可以KO掉若干人. 问最少选多少个角色( ...
- POJ 3398 Perfect Service --最小支配集
题目链接:http://poj.org/problem?id=3398 这题可以用两种上述讲的两种算法解:http://www.cnblogs.com/whatbeg/p/3776612.html 第 ...
- POJ3659 Cell Phone Network(树上最小支配集:树型DP)
题目求一棵树的最小支配数. 支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻. 听说即使是二分图,最小支配集的求解也是还没多项式算法的.而树上求最小支配集树型DP就OK了. ...
- POJ 3398 Perfect Service(树型动态规划,最小支配集)
POJ 3398 Perfect Service(树型动态规划,最小支配集) Description A network is composed of N computers connected by ...
- POJ 3659 Cell Phone Network / HUST 1036 Cell Phone Network(最小支配集,树型动态规划,贪心)-动态规划做法
POJ 3659 Cell Phone Network / HUST 1036 Cell Phone Network(最小支配集,树型动态规划,贪心) Description Farmer John ...
- 求解任意图的最小支配集(Minimun Dominating Set)
给定一个无向图G =(V,E),其中V表示图中顶点集合,E表示边的集合.G的最小控制顶点集合为V的一个子集S∈V:假设集合R表示V排除集合S后剩余顶点集合,即R∩S=∅,R∪S=V:则最小控制顶点集合 ...
- POJ 3659 Cell Phone Network(树的最小支配集)(贪心)
Cell Phone Network Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6781 Accepted: 242 ...
- 树形DP求树的最小支配集,最小点覆盖,最大独立集
一:最小支配集 考虑最小支配集,每个点有两种状态,即属于支配集合或者不属于支配集合,其中不属于支配集合时此点还需要被覆盖,被覆盖也有两种状态,即被子节点覆盖或者被父节点覆盖.总结起来就是三种状态,现对 ...
- 树形DP 树的最小支配集,最小点覆盖与最大独立集
最小支配集: 从V中选取尽量少的点组成一个集合,让V中剩余的点都与取出来的点有边相连. (点) 最小点覆盖: 从V中选取尽量少的点组成一个集合V1,让所有边(u,v)中要么u属于V1,要么v属于V1 ...
随机推荐
- P2878 [USACO07JAN]保护花朵Protecting the Flowers
一个类似国王游戏的贪心 话说要是先做了这个题,国王游戏之余懵逼这么久吗? #include<iostream> #include<cstdio> #include<alg ...
- js循环读取json数据,将读取到的数据用js写成表格
①js循环读取json数据的方式: var data=[{"uid":"2688","uname":"*江苏省南菁高级中学 022 ...
- java基础30问
Java基础知识30问 1. 面向对象和面向过程的区别 面向过程 优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机.嵌入式开发.Linux/Unix等一般采用 ...
- jquery操作DOM 元素(2)
.after() 在匹配的元素集合中的每个元素后面插入参数指定的内容,作为其兄弟节点. .after(content[,content]) content HTML字符串 DOM 元素 元素数组 对象 ...
- mysql存储问题
为什么标题要起这个名字呢?commen sence指的是那些大家都应该知道的事情,但往往大家又会会略这些东西,或者对这些东西一知半解,今天我总结下自己在mysql中遇到的一些commen sense类 ...
- 学习vue-cli3的项目搭建
安装 关于旧版本 Vue CLI 的包名称由 vue-cli 改成了 @vue/cli. 如果你已经全局安装了旧版本的 vue-cli(1.x 或 2.x),你需要先通过 npm uninstall ...
- 《Linux就该这么学》,刘小伙实在人,给打个广告
本书是由全国多名红帽架构师(RHCA)基于最新Linux系统共同编写的高质量Linux技术自学教程,极其适合用于Linux技术入门教程或讲课辅助教材,目前是国内最值得去读的Linux教材,也是最有价值 ...
- redis操作帮助类
RedisHelper.java import redis.clients.jedis.*; import java.util.*; public class RedisHelper { privat ...
- php-安装与配置-未完待续2
一,准备工作 在入门指引中,我们已经知道PHP的3个应用领域,不同的场景,需要安装的东西是不同的.具体如下: 服务器端脚本,在通常情况下,需要三样东西:PHP 自身.一个 web 服务器和一个 web ...
- 虚拟机linux桥接联网问题
Linux系统为redhat5.8 虚拟机的版本:vm8.0 本人刚刚开始接触linux,今日需要通过linux进行联网,因此也学习了一点点关于虚拟机的联网的知识,在此与大家进行分享,希望大家可以之处 ...