ZROI week1
$$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