【题目链接】点击打开链接
【HDU 5090 A】 水题,可以贪心调整好符合条件的,然后排序之后判断。其实仔细思考发现,这就是一个裸二分匹配!可以看下图: 对应第二个样例。。
【代码1 贪心】
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int a[110];
bool vis[110];
int m, n, k;
int main()
{
scanf("%d",&m);
while(m--){
scanf("%d%d",&n,&k);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++){
while(1){
if(a[i] > n) break;
if(!vis[a[i]]){
vis[a[i]] = 1;
break;
}
a[i] += k;
}
}
//xx
sort(a+1, a+n+1);
bool ok = 1;
for(int i = 1; i <= n; i++){
if(a[i] != i){
ok = 0;
break;
}
}
if(ok){
puts("Jerry");
}
else{
puts("Tom");
}
}
return 0;
}
【代码2 最大匹配】
bool con[110][110], vis[110];
int match[maxn], n, k, x;
bool dfs(int x)
{
for(int i = 1; i <= n; i++){
if(!vis[i] && con[x][i]){
vis[i] = 1;
if(match[i] == -1 || dfs(match[i]))
{
match[i] = x;
return true;
}
}
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
memset(con, 0, sizeof(con));
memset(match, -1, sizeof(match));
for(int i = 1; i <= n; i++){
scanf("%d", &x);
while(x <= n)
{
con[x][i] = 1;
x += k;
}
}
int ans = 0;
for(int i = 1; i <= n; i++){
memset(vis, 0, sizeof(vis));
if(dfs(i)) ans++;
}
if(ans == n){
printf("Jerry\n");
}
else{
printf("Tom\n");
}
}
return 0;
}
【B HDU 5091 】和今年在大连热身赛的题目一样,都可以转化成线段树扫描线来解!可以参考:点击打开链接
【代码】
int n, w, h;
struct node{
int l, r;
int num, lazy;
node(){}
node(int num, int lazy) : num(num), lazy(lazy){}
}Tree[20*maxn];
struct seg{
int x, y, v;
seg(){}
seg(int x, int y, int v) : x(x), y(y), v(v){}
bool operator<(const seg &rhs){
if(x == rhs.x) return v > rhs.v;
return x < rhs.x;
}
}seg[maxn*4];
void pushup(int rt)
{
Tree[rt].num = max(Tree[rt*2].num, Tree[rt*2+1].num);
}
void pushdown(int rt)
{
if(Tree[rt].lazy != 0){
Tree[rt*2].lazy += Tree[rt].lazy;
Tree[rt*2+1].lazy += Tree[rt].lazy;
Tree[rt*2].num += Tree[rt].lazy;
Tree[rt*2+1].num += Tree[rt].lazy;
Tree[rt].lazy = 0;
}
}
void Build(int l, int r, int rt)
{
Tree[rt].l = l, Tree[rt].r = r, Tree[rt].lazy = Tree[rt].num = 0;
if(l == r){
return ;
}
int mid = (l + r) / 2;
Build(l, mid, rt*2);
Build(mid+1, r, rt*2+1);
}
void update(int i, int rt)
{
if(Tree[rt].l >= seg[i].y && Tree[rt].r <= seg[i].y + h){
Tree[rt].num += seg[i].v;
Tree[rt].lazy += seg[i].v;
return ;
}
pushdown(rt);
int mid = (Tree[rt].l + Tree[rt].r) / 2;
if(seg[i].y <= mid) update(i, rt*2);
if(mid < seg[i].y + h) update(i, rt*2+1);
pushup(rt);
}
//void update(int i, int l, int r, int rt)
//{
// if(l >= seg[i].y && r <= seg[i].y + h){
// Tree[rt].num += seg[i].v;
// Tree[rt].lazy += seg[i].v;
// return ;
// }
// pushdown(rt);
// int mid = (Tree[rt].l + Tree[rt].r) / 2;
// if(seg[i].y <= mid) update(i, l, mid, rt*2);
// if(mid < seg[i].y + h) update(i, mid+1, r, rt*2+1);
// pushup(rt);
//}
int main()
{
while(scanf("%d",&n) != EOF && n > 0)
{
scanf("%d%d", &w, &h);
for(int i = 1; i <= n; i++){
int x, y;
scanf("%d%d", &x, &y);
seg[2*i - 1].x = x;
seg[2*i - 1].y = y + 2*maxn;
seg[2*i - 1].v = 1;
seg[2*i].x = x + w;
seg[2*i].y = y + 2*maxn;
seg[2*i].v = -1;
}
sort(seg+1, seg+2*n+1);
Build(1, 4*maxn, 1);
int ans = 0;
for(int i = 1; i <= 2*n; i++){
update(i, 1);
ans = max(ans, Tree[1].num);
}
printf("%d\n",ans);
}
return 0;
}
【C HDU 5092】题意是:给一个N*M的矩阵,让你用一条线 从第1行连到第N行(每行只能选一个元素),要求这条线所经过的元素之和最小。
有以下规定——
1,若你选择了位置(i,j)的元素,那么下一行的元素你只能选择(i+1,j-1)、(i+1,j)、(i+1,j+1)三个之一(当然边界只能选两个)。
2,若可以找到多条 路径上元素之和最小 的线,那么你要优先选择最右边的线。
3,最后从第一行开始 输出线上元素的纵坐标。
【解题方法】 简单DP+路径输出。。其实就是数塔问题的一个变形嘛,dp[i][j]表示经过i,j点可以得到的最小值!转移过程就是:
if(j != m && dp[i][j] > dp[i-1][j+1] + a[i][j]){
dp[i][j] = dp[i-1][j+1] + a[i][j];
path[i][j] = j+1;
}
if(dp[i][j] > dp[i-1][j] + a[i][j]){
dp[i][j] = dp[i-1][j] + a[i][j];
path[i][j] = j;
}
if(j != 1 && dp[i][j] > dp[i-1][j-1] + a[i][j]){
dp[i][j] = dp[i-1][j-1] + a[i][j];
path[i][j] = j-1;
}
【AC代码】
int dp[maxn][maxn], path[maxn][maxn], a[maxn][maxn], n, m;
void solve(int x, int y)
{
if(x == 1){
printf("%d ",y);
return ;
}
solve(x-1, path[x][y]);
if(x == n) printf("%d",y);
else printf("%d ",y);
}
int main()
{
int T, ks = 1;
scanf("%d",&T);
while(T--){
//memset(path, -1, sizeof(path));
for(int i = 0; i < maxn; i++){
for(int j = 0; j < maxn; j++){
dp[i][j] = inf;
}
}
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
scanf("%d",&a[i][j]);
}
}
for(int i = 1; i <= m; i++){
dp[1][i] = a[1][i];
path[1][i] = i;
}
for(int i = 2; i <= n; i++){
for(int j = 1; j <= m; j++){
if(j != m && dp[i][j] > dp[i-1][j+1] + a[i][j]){
dp[i][j] = dp[i-1][j+1] + a[i][j];
path[i][j] = j+1;
}
if(dp[i][j] > dp[i-1][j] + a[i][j]){
dp[i][j] = dp[i-1][j] + a[i][j];
path[i][j] = j;
}
if(j != 1 && dp[i][j] > dp[i-1][j-1] + a[i][j]){
dp[i][j] = dp[i-1][j-1] + a[i][j];
path[i][j] = j-1;
}
}
}
int ans = inf, pos;
for(int i = m; i >= 1; i--){
if(ans > dp[n][i]){
ans = dp[n][i];
pos = i;
}
}
printf("Case %d\n",ks++);
solve(n, pos);
printf("\n");
}
return 0;
}
【D HDU 5093】题意:给你一个n*m的图,图中有冰山 ‘# ’,浮冰 ‘o’ 以及普通海 ‘ * ’,现在要在海中布置尽可能多的炮弹,炮弹不能突波冰山,不能让炮弹互相攻击到,问最大能不知多少个?【解题方法】二分图的经典题目,关键在于怎么建图,图进行两次编号,按行编号,每一行中能攻击到的一块编号成相同的数,每一列同样,然后对行和列有编号的地方进行连边,求一次最大匹配即可,我用最大流求的最大匹配。
【AC代码】
int mp[2510][2510];
char s[52][52];
bool vis[2510];
int match[2510];
int x[52][52], y[52][52], n, m, cnt1, cnt2;
bool dfs(int x)
{
for(int i = 1; i < cnt2; i++){
if(mp[x][i] && !vis[i]){
vis[i] = 1;
if(match[i] == -1 || dfs(match[i])){
match[i] = x;
return true;
}
}
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(x, 0, sizeof(x));
memset(y, 0, sizeof(y));
memset(mp, 0, sizeof(mp));
scanf("%d%d",&n,&m);
for(int i = 0; i < n; i++) scanf("%s",s[i]);
cnt1 = 1;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(s[i][j] == '*') x[i][j] = cnt1;
if(s[i][j] == '#') cnt1++;
}
cnt1++;
}
cnt2 = 1;
for(int j = 0; j < m; j++){
for(int i = 0; i < n; i++){
if(s[i][j] == '*') y[i][j] = cnt2;
if(s[i][j] == '#') cnt2++;
}
cnt2++;
}
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(s[i][j] == '*'){
mp[x[i][j]][y[i][j]] = 1;
}
}
}
memset(match, -1, sizeof(match));
int ans = 0;
for(int i = 1; i < cnt1; i++){
memset(vis, 0, sizeof(vis));
if(dfs(i)) ans++;
}
printf("%d\n",ans);
}
return 0;
}
【E HDU 5094】【题意】有一个N*M的地图,图里面有p种门。
下一行输入一个k,代表有k个门 (或墙)。下面k行给出它们所在的位置,样例(x1,x2,y1,y2,op)意义:前四个数值分别代表两个位置的坐标,当op为0时说明两位置之间是一堵墙,当op大于0时说明两位置之间是一扇门且op代表门的型号。
然后输入一个S,代表钥匙的数目,接下来S行每行三个数(x,y,op)代表位置(x,y)有 一把型号op的门 的钥匙。
问你能够从(1,1)出发到达(N,M),若可以输出最小步数,否则输出-1。当然每个点是可以无限走的。
【解体方法】简单BFS + 状态压缩。注意状态压缩处理二进制时,对门和钥匙的编号要减一处理。=注意此题有坑处:
1:每个点可能有 很多把 相同型号或者不同型号的钥匙。我是开了二维数组记录没个点的钥匙状态!
2:起点可能有钥匙,注意初始状态的处理。
【AC 代码】
int dir[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};
int n, m, p;
int g[maxn][maxn][maxn][maxn], key[maxn][maxn];
bool vis[maxn][maxn][maxm];
struct node{
int x, y, state, step;
node(){}
node(int x, int y, int state, int step):x(x), y(y), state(state), step(step){}
};
bool check(int x, int y)
{
if(x >= 1 && x <= n && y >= 1 && y <= m) return true;
else return false;
}
int bfs();
int main()
{
while(scanf("%d%d%d",&n,&m,&p)!=EOF)
{
memset(key, 0, sizeof(key));
memset(g, -1, sizeof(g));
memset(vis, false, sizeof(vis));
int k;
scanf("%d",&k);
while(k--)
{
int x1, y1, x2, y2, st;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&st);
g[x1][y1][x2][y2] = g[x2][y2][x1][y1] = st;
}
int s;
scanf("%d",&s);
while(s--)
{
int x, y, q;
scanf("%d%d%d",&x,&y,&q);
q--;
key[x][y] |= (1<<q);
}
int ans = bfs();
printf("%d\n", ans);
}
return 0;
}
int bfs()
{
queue<node>q;
node now, nex;
now = node(1, 1, key[1][1], 0);
vis[1][1][key[1][1]] = 1;
q.push(now);
while(!q.empty())
{
now = q.front();
q.pop();
int x = now.x, y = now.y, sta = now.state, s = now.step;
if(x == n && y == m) return s;
for(int i = 0; i < 4; i++){
int dx = x + dir[i][0];
int dy = y + dir[i][1];
int sta2 = sta | key[dx][dy];
int t = g[x][y][dx][dy];
if(check(dx, dy) && ((t == -1) || ((1<<(t-1))&sta2 && t > 0)) && !vis[dx][dy][sta2]){
vis[dx][dy][sta2] = 1;
q.push(node(dx, dy, sta2, s+1));
}
}
}
return -1;
}
【F HDU 5095】模拟不说,就是题意很恶心,细心读。
【AC代码】
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int a[11];
char s[11] = {' ','p','q','r','u','v','w','x','y','z'};
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int pos;
memset(a, 0, sizeof(a));
for(int i = 1; i <= 10; i++){
scanf("%d",&a[i]);
}
for(int i = 1; i <= 10; i++){
if(a[i] != 0){
pos = i;
break;
}
}
if(a[pos] == 0){
printf("\n");
continue;
}
if(pos != 10){
if(a[pos] == -1){
printf("-%c",s[pos]);
}
else {
if(a[pos] == 1){
printf("%c",s[pos]);
}else printf("%d%c",a[pos],s[pos]);
}
}
else{
printf("%d", a[pos]);
}
for(int i = pos+1; i <= 10; i++){
if(i < 10){
if(a[i] == 0) continue;
if(a[i] > 0){
if(a[i] == 1){
printf("+%c",s[i]);
}else
printf("+%d%c",a[i],s[i]);
}
else{
if(a[i] == -1){
printf("-%c",s[i]);
}
else
printf("%d%c",a[i],s[i]);
}
}
else if( i == 10){
if(a[i] == 0) break;
else if(a[i] > 0) printf("+%d",a[i]);
else printf("%d",a[i]);
}
}
printf("\n");
}
return 0;
}
【G和H不会】
【I HDU 5098】题意:软件在安装之后需要重启才能发挥作用,现在给你一堆软件(有的需要重启有的不需要)以及安装这个软件之前需要哪些软件发挥作用,求最少的重启次数?【解法】来自斌神的解法:可以看出是拓扑排序,但是需要用两个队列q1,q2分别来存 不需要重启的software 和 需要重启的software。根据题目输入建好图后,按照拓扑序规则,首先将入度的0的点加进队列,不需要重启的进q1,需要的进q2。然后处理q1中的所有节点(即要删除这些点),那么要让与他们相连的节点的入度减1,如果发现减完入度为0,再判断其是否需要重启,并加进q1 or q2。一旦发现q1为空,那么使答案加1(不能继续任何安装即重启一次),把q2中所有元素加入q1,q2清空。如此循环直到q1,q2均为空即可。还有一种解法:就是DAG的最长路。有兴趣可以搜来学习一下。
【AC代码】
//
//Created by just_sort 2016/12/4
//Copyright (c) 2016 just_sort.All Rights Reserved
//
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
using namespace std;
using namespace __gnu_pbds;
typedef long long LL;
typedef pair<int, LL> pp;
#define MP(x,y) make_pair(x,y)
const int maxn = 1020;
const int maxm = 1<<12;
const int inf = 0x3f3f3f3f;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>order_set;
//head
map <string, int> mp;
int cnt, edgecnt, reboot[maxn], out[maxn], in[maxn];
int head[maxn], vis[maxn];
struct edge{
int v, next;
}E[maxn*maxn];
void addedge(int u, int v)
{
E[edgecnt].v = v, E[edgecnt].next = head[u], head[u] = edgecnt++;
}
void init()
{
mp.clear();
memset(reboot, 0, sizeof(reboot));
memset(out, 0, sizeof(out));
memset(in, 0, sizeof(in));
memset(head, -1, sizeof(head));
cnt = edgecnt = 0;
}
int topsort()
{
queue<int>q1, q2;
for(int i = 1; i <= cnt; i++){
if(in[i] == 0){
if(reboot[i] == 0) q1.push(i);
else q2.push(i);
}
}
int ans = 0;
while(!q1.empty() || !q2.empty()){
if(q1.empty() && !q2.empty()){
ans++;
while(!q2.empty()){
q1.push(q2.front());
q2.pop();
}
}
while(!q1.empty()){
int u = q1.front(); q1.pop();
for(int i = head[u]; ~i; i = E[i].next){
int v = E[i].v;
in[v]--;
if(in[v] == 0){
if(reboot[v] == 0){
q1.push(v);
}
else{
q2.push(v);
}
}
}
}
}
return ans;
}
int main()
{
string s;
char name[1010];
int T, ks = 1;
scanf("%d",&T);
getchar();
getchar();
while(T--){
init();
while(getline(cin, s)){
if(s[0] == '\0') break;
istringstream sin(s);
sin >> name;
int len = strlen(name);
int flag = 0;
if(name[len - 2] == '*'){
flag = 1;
name[len - 2] = '\0';
}
else{
name[len - 1] = '\0';
}
string a, b;
a = name;
if(mp.find(a) == mp.end()) mp[a] = ++cnt;
reboot[mp[a]] = flag;
while(sin >> b){
if(mp.find(b) == mp.end()) mp[b] = ++cnt;
addedge(mp[b], mp[a]);
out[mp[b]]++;
in[mp[a]]++;
}
}
int ans = topsort();
printf("Case %d: %d\n", ks++, ans);
}
return 0;
}
【J HDU 5099】模拟,直接上代码了! 【代码】
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
char a[10], b[10];
char c[10], d[10];
int main()
{
int T, ks = 1;
scanf("%d",&T);
while(T--){
memset(c, '\0', sizeof(c));
memset(d, '\0', sizeof(d));
scanf("%s %s", a, b);
printf("Case %d: ",ks++);
if(a[0] < b[0]) printf("< ");
else if(a[0] == b[0]) printf("= ");
else printf("> ");
if(a[1] == b[1]){
strncpy(c, a+2, 4);
strncpy(d, b+2, 4);
if(strcmp(c, d) > 0) printf(">");
else if(strcmp(c, d) == 0) printf("=");
else printf("<");
}
else{
strncpy(c, a+2, 3);
strncpy(d, b+2, 3);
if(strcmp(c, d) > 0) printf(">");
else if(strcmp(c, d) == 0) printf("=");
else printf("<");
}
printf("\n");
}
return 0;
}