博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

ZROI week1

\[ZROI day1 \]

$$Grid$$

  • 题目描述
    给定一个矩阵,小写字母,求一条路径使得从\((1,1) -> (n,m)\),字典序最小,并且每次只能向右或者向下。

  • 题解

先考虑如果没有重复字母,可以再\(dfs\)的过程中不断贪心得到路径。

如果有重复的话,考虑枚举每条对角线,求出到这条对角线的最小字典序路径和所有可能的结束位置。

复杂度\(O(n \times m)\)

还有一种就是考虑预处理出每一步最接近答案的最远点,进行bfs即可。

ps:忘记\(unique\),考试的时候T1调了一个半小时多。。。

$$water$$

  • 题目描述

  • 题解
    裸状压dp题。

ps:这题打挂了就20分

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 30;
const int INF = (1 << 30) - 1;
int c[MAXN][MAXN];
#define rep(i,_,__)for(int i = _;i <= __; ++i)
#define down(i,_,__) for(int i = _;i >= __; --i)
const int U = 1 << 21;


int f[U];
int Count(int state) {
	bitset <100> bit;
	bit = state;
	return bit.count();
}

int n;
int k;
#define all (1 << n)
int ans = INF;
int main () {
	cin >> n >> k;
	rep(i,0,n - 1) {
		rep(j,0,n - 1) {
			cin >> c[i][j];
		}
	}
	for(int i = 0;i <= all; ++i) f[i] = INF;
	f[all - 1] = 0;
	down(state,all - 1,0) {
		//cout<<Count(state)<<endl;
		rep(i,0,n - 1) {
			if(state & (1 << i)) {
				rep(j,0,n - 1) {
					if(i == j) continue;
					f[(state - (1 << i)) | (1 << j)] = min(f[(state - (1 << i)) | (1 << j)],f[state] + c[i][j]);
				}
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

$$pal$$

  • 题目描述
    给定一个序列,每次可以合并相邻的数字,合并之后是一个新数等于原数之和,求最少合并次数使得变成一个回文序列。

  • 题解
    指针扫描即可。

证明:
给定序列:1 5 3 2 1
考虑分治这个过程,第一个和最后一个相等,那么这个东西就永远不能动,否则会增加次数,如果相等把指针l ++,r --,如果不一样找到小的往前合并,直到相等,类似解子问题的思路。

ps:一眼+5min证明

$$LIS$$

  • 题目描述
    按照一定规则构造序列,求构造出的所有序列中LIS长度和数目。

  • ps:先来ps一下,想出来了没时间写,中途去干了某些事情QAQ

  • 题解
    长度好说,贪心构造就行。
    怎么算方案数?
    经典计数问题,设\(f_i\)表示以\(i\)结尾的LIS长度,\(g_i\)表示\(f_i\)的方案数
    略写转移即可,虽然比较难写。

\[作业 \]

$$T1 : poj 2411$$

记录放的状况转移即可。

#include <cstdio>
using namespace std;
#define ll long long
const int MAXN = 12;
ll f[MAXN][1 << MAXN];
bool state[1 << MAXN];
#define U 1 << m
int n,m;
int main () {
	while(~scanf("%d %d",&n,&m)) {
		if(!n and !m) return 0;
		for(int i = 0;i < U; ++i) {
			bool odd = 0;
			bool stp = 0;
			for(int j = 0;j < m; ++j) {
				if(i >> j & 1) {
					odd |= stp;
					stp = 0;
				}
				else stp ^= 1;
			}
			state[i] = odd | stp ? 0 : 1;
		}
		f[0][0] = 1;
		for(int i = 1;i <= n; ++i) {
			for(int j = 0;j < U; ++j) {
				f[i][j] = 0;
				for(int k = 0;k < U; ++k) {
					if((j & k) == 0 and state[j | k]) {
						f[i][j] += f[i - 1][k];
					}
				}
			}
		}
		printf("%lld\n",f[n][0]);
	}
	return 0;
}

$$T2 : hdu 6006$$

枚举每个工程师的情况,做背包即可。

调了半天...

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 20;
#define ll long long
#define C continue
ll f[MAXN][1 << 11];
vector<int>v[MAXN],a[MAXN],b[MAXN];

int vis[300];
void Clear() {
	memset(f,0,sizeof f);
	memset(vis,0,sizeof vis);
	for(int i = 0;i < 20; ++i) {
		v[i].clear();
		a[i].clear();
		b[i].clear();
	}
}
#define pb(x) push_back(x)
int n,T,m;
int read () {
	int q=0,f=1;char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;ch=getchar();
	}
	while(isdigit(ch)) {
		q=q*10+ch-'0';ch=getchar();
	}
	return q*f;
}
int cas;
int main () {
	T = read();
	while(T--) {
		Clear();
		n = read(),m = read();
		for(int i = 1;i <= n; ++i) {
			int num = read();
			for(int j = 1;j <= num; ++j) {
				a[i].pb(read());
			}
		}
		for(int i = 1;i <= m; ++i) {
			int num = read();
			for(int j = 1;j <= num; ++j) {
				b[i].pb(read());
			}
		}
		for(int i = 1;i <= n; ++i) {
			for(int j = 0;j < (1 << m); ++j) {
				memset(vis,0,sizeof vis);
				int cnt = 0;
				for(int k = 0;k < m; ++k) {
					if(j & (1 << k)) {
						cnt ++;
						for(int l = 0;l < b[k + 1].size(); ++l) {
							vis[b[k + 1][l]] ++;
						}
					}
				}
				if(cnt > 3) C;
				bool tag = 1;
				for(int k = 0;k < a[i].size(); ++k) {
					if(vis[a[i][k]] == 0) {
						tag = 0;
					}
				}
				if(tag == 1) v[i].pb(j);
			}
		}
		for(int state = 0;state < (1 << m); ++state) {
			for(int j = 0;j < v[1].size(); ++j) {
				if((state | v[1][j]) == state) {
					f[1][state] = 1;
				}
			}
		}
		for(int i = 2;i <= n; ++i) {
			for(int state = 0 ;state < (1 << m); ++ state) {
				for(int j = 0;j < v[i].size(); ++j) {
					if((state | v[i][j]) == state) {
						f[i][state] = max(f[i][state],f[i - 1][state - v[i][j]] + 1);
					}
				}
				f[i][state] = max(f[i][state],f[i - 1][state]);
			}
		}
		printf("Case #%d: %lld\n",++cas, f[n][(1 << m) - 1]);
	}
	return 0;
}

$$T3 : hdu 4640$$

考虑把每个人移动的状态压成二进制,dp即可。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 21;
const int U = (1 << 18);
const int INF = 0x3f3f3f3f;
int T;
int x,y,z;
typedef pair<int,int> P;
int a[MAXN][MAXN];
int sum;
int n,m;
int ans;
int Q;
int f[U][MAXN];
int g[4][U];
int vis[U][MAXN];
queue<P>q;
int cas;
#define mk(x,y) make_pair(x,y)
#define fir first
#define sec second
void bfs() {
	q.push(mk(0,0));
	memset(vis,0,sizeof vis);
	for(int i = 0;i < (1 << n); ++i) {
		for(int j = 0;j < n; ++j) {
			f[i][j] = INF;
		}
	}
	f[0][0] = 0;
	while(!q.empty()) {
		P now = q.front();
		q.pop();
		int start = now.fir;
		int end = now.sec;
		vis[start][end] = 0;
		for(int i = 0;i < n; ++i) {
			if(a[end][i] < INF && f[start | (1 << i)][i] > f[start][end] + a[end][i]) {
				f[start | (1 << i)][i] =f[start][end] + a[end][i];
				if(vis[start | (1 << i)][i] == 0) {
					vis[start | (1 << i)][i] = 1;
					q.push(mk(start | (1 << i),i));
				}
			}
		}
	}
}

int main () {
	cin >> T;
	while(T--) {
		cin >> n >> m;
		for(int i = 0;i <= n; ++i) {
			for(int j = 0;j <= n; ++j) {
				a[i][j] = INF;
				if(i == j) a[i][j] = 0;
			}
		}
		for(int i = 1;i <= m; ++i) {
			cin >> x >> y >> z;
			-- x;
			-- y;
			a[x][y] = a[y][x] =min(a[x][y],z);
		}
		sum = 0;
		cin >> Q;
		while(Q--) {
			int state;
			cin >> state;
			sum |= (1 << (state - 1));
		}
		bfs();
		printf("Case %d: ",++cas);
		for(int i = 1;i <= 3; ++i) {
			for(int j = 0;j < (1 << n); ++j) {
				g[i][j] = INF;
			}
		}
		for(int i = 0;i < (1 << n); ++i) {
			for(int j = 0;j <n; ++j) {
				g[1][i] = min(g[1][i],f[i][j]);
			}
		}
		for(int i = 2;i <= 3; ++i) {
			for(int j = 0;j < (1 << n); ++j) {
				for(int k = j; k ;k = (k - 1) & j) {
					g[i][j] = min(g[i][j],max(g[1][k],g[i - 1][j ^ k]));
				}
			}
		}
		ans = INF;
		for(int i = 1;i <= 3; ++i) {
			for(int j = 0;j < (1 << n); ++j) {
				if((sum & j) == sum) {
					ans = min(ans,g[i][j]);
				}
			}
		}
		if(ans == INF) {
			puts("-1");
		}
		else cout<<ans<<endl;
	}
	return 0;
}

$$T4 : hdu 4462$$

枚举每个合法状态,暴力即可。

ps:hdu居然不认and操作

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <bitset>
#define C continue
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 51;
const int MAXM = 11;
bitset<3000>bit[MAXM],state;
struct position {
	int x;
	int y;
}p[MAXN << 1];
bool find(int x,int y,int z) {
	return x + y <= z;
}
int n,k;
int ans;
int Range[MAXN * MAXN];
int vis[MAXN][MAXN];
int x,y;
int main () {
	while(cin >> n && n) {
		cin >> k;
		memset(vis,0,sizeof vis);
		for(int i = 1;i <= k; ++i) {
			cin >> x >> y;
			p[i].x = x;
			p[i].y = y;
			vis[p[i].x][p[i].y] = 1;
		}
		for(int i = 1;i <= k; ++i) {
			cin >> Range[i];
		}
		for(int i = 1;i <= k; ++i) {
			bit[i].reset();
			for(int j = 1;j <= n; ++j) {
				for(int k = 1;k <= n; ++k) {
					if(vis[j][k]) {
						C;
					}
					if(find(abs(j - p[i].x),abs(k - p[i].y),Range[i])) {
						bit[i].set((j - 1) * n + k - 1);
					}
				}
			}
		}
		ans = INF;
		for(int i = 0;i < (1 << k); ++i) {
			int cnt = 0;
			state.reset();
			for(int j = 1;j <= k; ++j) {
				if(i & (1 << (j - 1))) {
					cnt ++;
					state |= bit[j];
				}
			}
			if(state.count() == n * n - k) {
				ans = min(ans,cnt);
			}
		}
		if(ans == INF) {
			cout<<-1<<endl;
		}
		else cout<<ans<<endl;
	}
}

$$T5 : hdu 3017$$

考虑是\(^{30}\),我们用双向搜索,每次让两个方向的搜索选一个状态,最后拼起来判断即可。
(基本套路)

finished

posted @ 2018-12-16 14:31  Allorkiya  阅读(160)  评论(0编辑  收藏  举报