例题8-1
采用直接构造法,也就是经验方法求解,通过猜想感觉经验求解。没有一个通用的模板。
#include
using namespace std;
const int maxn=100;
int pancake[maxn],ans[maxn];
int len=0,ansi=0;
int flip(int index)
{
int tmp[maxn];
for(int i=0;i<=index;i++)
{
tmp[i]=pancake[index-i];
}
for(int i=0;i<=index;i++)
{
pancake[i]=tmp[i];
}
}
void findmaxnumber(int l,int j)
{
int maxnumber=pancake[0],maxindex=0;
for(int i=1;i>in)
{
if(flag>0)
cout<<" ";
pancake[len++]=in;
cout<
例题8-2
本题目也是采用经验法,进行直接构造。
#include
using namespace std;
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
int n;
while(cin>>n)
{
cout<<"2"<<" "<
例题8-3
本题最好使用hash,但是一直不能设计好hash函数,使用map会超时,而本题使用的存储方式,一定要将fl声明为全局变量,如果是局部变量会报错,编译器不会提供那么大的空间用于存储。因为有大量的数据读入,所以使用ios::sync_with_stdio(false); 关闭同步,增加速度。
#include
using namespace std;
const int maxn=5000;
long long fl[4005*4005];
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
ios::sync_with_stdio(false);
long long A[maxn],B[maxn],C[maxn],D[maxn];
int m,flag=0;
cin>>m;
while(m--)
{
if(flag)
{
cout<>n;
long long ans=0;
for(int i=0;i>A[i]>>B[i]>>C[i]>>D[i];
}
int r,c,z;
long long len=0;
for(int i=0;i
例题8-4
枚举超时,枚举代码:
#include
using namespace std;
const int maxn=5000+100;
int xl[maxn],xr[maxn],yl[maxn],yr[maxn];
int visX[maxn],visY[maxn],n;
bool dfsx(int *A,int cur)
{
if(cur==n)
{
return true;
}
for(int i = xl[cur];i<=xr[cur];i++)
{
if(visX[i]) continue;
else
{
visX[i]=1;
A[cur]=i;
if(dfsx(A,cur+1))
return true;
visX[i]=0;
}
}
return false;
}
bool dfsy(int *A,int cur)
{
if(cur==n)
{
return true;
}
for(int i = yl[cur];i<=yr[cur];i++)
{
if(visY[i]) continue;
else
{
visY[i]=1;
A[cur]=i;
if(dfsy(A,cur+1))
return true;
visY[i]=0;
}
}
return false;
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
while(cin>>n&&n)
{
memset(visX,0,sizeof(visX));
memset(visY,0,sizeof(visY));
for(int i=0;i>xl[i]>>yl[i]>>xr[i]>>yr[i];
}
int X[maxn],Y[maxn];
if((!dfsx(X,0))||(!dfsy(Y,0)))
cout<<"IMPOSSIBLE"<
所以,本题可以采用贪心策略,因为贪心策略的使用,也只是一种猜想,当然在编程前能证明是最好的。本次采用的贪心策略是先将各区间进行按照xr从小到达排序,xr相等的,xl较小的排在前面。排序后的各点在格子区间里面选择尽量小的数作为自己的行数(列数)。目的是让先选择的车,选择的行(列)对后面的车的选择影响最小。
#include
using namespace std;
const int maxn=5000+100;
struct rook
{
int xl,xr,yl,yr;
int id;
};
rook rooks[maxn],rookssort[maxn];
bool cmpx(rook a,rook b)
{
if(a.xr!=b.xr)
return a.xr>n&&n)
{
int ansx[maxn],ansy[maxn],findx=1,findy=1;
int visitx[maxn],visity[maxn];
memset(visitx,0,sizeof(visitx));
memset(visity,0,sizeof(visity));
for(int i=0;i>rooks[i].xl>>rooks[i].yl>>rooks[i].xr>>rooks[i].yr;
}
sort(rooks,rooks+n,cmpx);
for(int i=0;i
例题8-5
本题目通过等价转换后,代码变的简单。将一个大问题变小。
#include
using namespace std;
int main()
{
int n;
while(cin>>n&&n)
{
long long ans=0,a,last=0;
for(int i=0;i>a;
ans+=abs(last);
last+=a;
}
cout<
例题8-6
本题采用极点扫描法,参考了代码仓库的代码,对关键的扫描部分进行了注释。
// UVa1606 Amphiphilic Carbon Molecules
// Rujia Liu
// To make life a bit easier, we change each color 1 point into color 0.
// Then we only need to find an angle interval with most points. See code for details.
#include
#include
#include
#include
using namespace std;
const int maxn = 1000 + 5;
struct Point {
int x, y;
double rad; // with respect to current point
bool operator<(const Point &rhs) const {
return rad < rhs.rad;
}
}op[maxn], p[maxn];
int n, color[maxn];
// from O-A to O-B, is it a left turn?
bool Left(Point A, Point B) {
return A.x * B.y - A.y * B.x >= 0;
}
int solve() {
if(n <= 2) return 2;
int ans = 0;
// pivot point
for(int i = 0; i < n; i++) {
int k = 0;
// the list of other point, sorted in increasing order of rad
for(int j = 0; j < n; j++)
if(j != i) {
p[k].x = op[j].x - op[i].x;
p[k].y = op[j].y - op[i].y;
if(color[j]) { p[k].x = -p[k].x; p[k].y = -p[k].y; }
p[k].rad = atan2(p[k].y, p[k].x);
k++;
}
sort(p, p+k);
// sweeping. cnt is the number of points whose rad is between p[L] and p[R]
int L = 0, R = 0, cnt = 2;
while(L < k) {
//注意这个循环体内部cnt不会重置,那么在这个循环中,求解白点的数量,是通过
//在相对坐标系中,以原点和p[L]之间的连线作为分割线,以L=0为初始状态,通过扫描的
//方法(判断P[R]是否符合要求)计算出左侧白点的数量,当L=1的时候,在L=0时的白点加上符合L=1时分割线的白点,减去
//不符合要求的点(实际上只有一个点)。
if(R == L) { R = (R+1)%k; cnt++; }
while(R != L && Left(p[L], p[R])) { R = (R+1)%k; cnt++; } //增加符合要求的点
cnt--;//减去不符合要求的白点。
L++;
ans = max(ans, cnt);
}
}
return ans;
}
int main() {
freopen("datain.txt","r",stdin);
while(scanf("%d", &n) == 1 && n) {
for(int i = 0; i < n; i++)
scanf("%d%d%d", &op[i].x, &op[i].y, &color[i]);
printf("%d\n", solve());
}
return 0;
}
例题8-7
本题使用两种方法,进行求解。算法复杂度相同,引入了滑动窗口的概念。
第一种:
#include
using namespace std;
const int maxn = 1000000+5;
int A[maxn];
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i s;
int L=0,R=0,ans=0;
while(R
#include
using namespace std;
int A[maxn],last[maxn];
map cur;
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
cur.clear();
for(int i=0;i
例题8-8
本题基本采用等价转化的思想,并使用set来进行实现。本题列出代码仓库的代码。
// UVa1471 Defense Lines
// Rujia Liu
// Algorithm 1: use STL set to maintain the candidates.
// This is a little bit more intuitive, but less efficient (than algorithm 2)
#include
#include
#include
using namespace std;
const int maxn = 200000 + 5;
int n, a[maxn], f[maxn], g[maxn];
struct Candidate {
int a, g;
Candidate(int a, int g):a(a),g(g) {}
bool operator < (const Candidate& rhs) const {
return a < rhs.a;
}
};
set s;
int main() {
freopen("datain.txt","r",stdin);
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
if(n == 1) { printf("1\n"); continue; }
// g[i] is the length of longest increasing continuous subsequence ending at i
g[0] = 1;
for(int i = 1; i < n; i++)
if(a[i-1] < a[i]) g[i] = g[i-1] + 1;
else g[i] = 1;
// f[i] is the length of longest increasing continuous subsequence starting from i
f[n-1] = 1;
for(int i = n-2; i >= 0; i--)
if(a[i] < a[i+1]) f[i] = f[i+1] + 1;
else f[i] = 1;
s.clear();
s.insert(Candidate(a[0], g[0]));
int ans = 1;
for(int i = 1; i < n; i++) {
Candidate c(a[i], g[i]);
set::iterator it = s.lower_bound(c);
// first one that is >= c
bool keep = true;
if(it != s.begin()) {
Candidate last = *(--it);
// (--it) points to the largest one that is < c
int len = f[i] + last.g;
ans = max(ans, len);
s.insert(c);
}
if(keep) {
s.erase(c); // if c.a is already present, the old g must be <= c.g
it = s.find(c); // this is a bit cumbersome and slow but it's clear
it++;
while(it != s.end() && it->a > c.a && it->g <= c.g) s.erase(it++);
}
}
printf("%d\n", ans);
}
return 0;
}
例题8-9
本题目参考书中解答和代码仓库。
// UVa1451 Average
// Rujia Liu
#include
using namespace std;
const int maxn = 100000 + 5;
int n, L;
char s[maxn];
int sum[maxn], p[maxn]; // average of i~j is (sum[j]-sum[i-1])/(j-i+1)
// compare average of x1~x2 and x3~x4
int compare_average(int x1, int x2, int x3, int x4) {
return (sum[x2]-sum[x1-1]) * (x4-x3+1) - (sum[x4]-sum[x3-1]) * (x2-x1+1);
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d%s", &n, &L, s+1);
sum[0] = 0;
for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + s[i] - '0';
int ansL = 1, ansR = L;
// p[i..j) is the sequence of candidate start points
int i = 0, j = 0;
for (int t = L; t <= n; t++) { // end point
while (j-i > 1 && compare_average(p[j-2], t-L, p[j-1], t-L) >= 0) j--; // remove concave points
p[j++] = t-L+1; // new candidate
while (j-i > 1 && compare_average(p[i], t, p[i+1], t) <= 0) i++; // update tangent point
// compare and update solution
int c = compare_average(p[i], t, ansL, ansR);
if (c > 0 || c == 0 && t - p[i] < ansR - ansL) {
ansL = p[i]; ansR = t;
}
}
printf("%d %d\n", ansL, ansR);
}
return 0;
}
例题8-10
本题目采用的二分法和贪婪算法结合,通过二分法找到最大值尽量小的那个数,在通过贪心法从右向左尽量搜索,每个子序列在小于找到的那个数的情况,尽量长。这样就能保证S(1)尽量小,满足条件。
#include
using namespace std;
typedef long long ll;
const int maxn=600;
ll m,k,p[maxn],slash[maxn];
;
bool judge(ll mid,ll& maxnum)
{
for(int i=0;i=k-i-1;j--)
{
if(i!=k-1)
{
if(sum+p[j]<=mid)
{
sum+=p[j];
slash[i+1]=j;
}
else
{
break;
}
}
else
{
sum+=p[j];
slash[i+1]=j;
}
}
maxnum=max(maxnum,sum);
}
if(maxnum<=mid) return true;
else return false;
}
ll bsearch(ll x,ll y)
{
ll mid;
slash[0]=m;
while(x>T;
while(T--)
{
ll lb=0,ub=0,mid,maxnum=0;
cin>>m>>k;
for(int i=0;i>p[i];
ub+=p[i];
}
mid = bsearch(lb,ub);
judge(mid,maxnum);
int j=k-1;
for (int i=0;i0)
{
cout<<"/"<<" ";
j--;
}
cout<0)
{
cout<<"/"<<" ";
j--;
}
cout<
例题8-11
贪心算法,每次选择最小的两个数进行相加,并将他们的和压入优先队列。
#include
using namespace std;
const int maxn=6000;
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
int n;
while(cin>>n&&n)
{
int tmp;
priority_queue,greater> q;
for(int i=0;i>tmp;
q.push(tmp);
}
int ans = 0;
for(int i=0;i
习题8-12
我开始并没有使用提供的方法,我们可以发现规律,第k小时第i行。i>2^(k-1)的时候,红气球数量等于第k-1小时第i行的红气球数量,如果i<=2^(k-1)时,红气球数量等于第k-1小时第i行的红气球数量的2倍。这样求出每行再累加起来就可以得到答案。时间复杂度为O(k*(b-a)).经过UVa测试,LTE超时,还是不得不使用书中O(k)的思路。
第一种:TLE
#include
using namespace std;
typedef long long ll;
ll cntr(ll k, ll a)
{
if(k==0) return 1;
else if(a>(1<<(k-1)))
{
return cntr(k-1, a-(1<<(k-1)));
}
else
{
return cntr(k-1, a)*2;
}
}
int main()
{
//freopen("datain.txt","r",stdin);
// freopen("dataout.txt","w",stdout);
int T,rnd=1;
cin>>T;
while(T--)
{
ll k,a,b;
ll sum=0;
cin>>k>>a>>b;
for(ll i=a;i<=b;i++)
{
sum+=cntr(k,i);
}
cout<<"Case "<
第二种:
通过求解书中的f函数或者g函数都能求解,下面以g函数为例。本题使用了递推。
#include
using namespace std;
long long c(int k)
{
return k == 0 ? 1 : c(k - 1) * 3;
}
long long g(int k, int i)
{
if (i == 0) {
return 0;
}
if (k == 0) {
return 1;
}
int k2 = 1 << (k - 1);
if (i >= k2) {
return 2 * g(k - 1, i - k2) + c(k - 1);
}
else {
return g(k - 1, i);
}
}
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
ios::sync_with_stdio(false);
int T;
cin >> T;
int kase = 0;
while (T--) {
int k, a, b;
cin >> k >> a >> b;
cout << "Case " << ++kase << ": ";
cout << g(k, (1 << k) - a + 1) - g(k, (1 << k) - b) << endl;
}
return 0;
}
例题8-13
模拟法,主要是要从中挖掘和分析出一些限制条件,避免没有必要的枚举,节省时间。
#include
using namespace std;
typedef long long ll;
const ll maxn=100010;
int ps[maxn],qs[maxn];
int main()
{
//freopen("datain.txt","r",stdin);
//freopen("dataout.txt","w",stdout);
int T,rnd=1;
cin>>T;
while(T--)
{
cout<<"Case "<>n;
for(ll i=0;i>ps[i];
for(ll i=0;i>qs[i];
int su=1;
for(ll i=0;i=qs[j])
{
gas-=qs[j++];
if(j == n){j %= n;flag = 1;}
//表示已经遍历了N个点,如果能够成功那么cnt==n
//如果失败,那么证明以i...n作为起点也不会成功
cnt++;
}
else
{
i = j+1;break;
}
}
if(cnt == n)
{
cout<<"Possible from station "<
例题8-14
本题目自己对题意理解不深,使用的是代码仓库的代码,以供参考。
// UVa1607 Gates
// Rujia Liu
#include
#include
using namespace std;
const int maxm = 200000 + 5;
int n, m;
struct Gates {
int a, b, o;
} gates[maxm];
// returns the output of input 000..0111...1 (there are k 0's)
int output(int k) {
for(int i = 1; i <= m; i++) {
int a = gates[i].a;
int b = gates[i].b;
int va = a < 0 ? -a > k : gates[a].o;
int vb = b < 0 ? -b > k : gates[b].o;
gates[i].o = !(va && vb);
}
return gates[m].o;
}
// returns k such that
// 1. output(k) = output(n)
// 2. output(k-1) = output(0)
int solve(int vn) {
int L = 1, R = n;
while(L < R) {
int M = L + (R-L)/2;
if(output(M) == vn) R = M; else L = M+1;
}
return L;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
scanf("%d%d", &gates[i].a, &gates[i].b);
int v0 = output(0);
int vn = output(n);
if(v0 == vn) {
for(int i = 1; i <= n; i++) printf("0");
} else {
int x = solve(vn);
for(int i = 1; i < x; i++) printf("0");
printf("x");
for(int i = x+1; i <= n; i++) printf("1");
}
printf("\n");
}
return 0;
}
例题8-15
本题通过滑动窗口加枚举的方式,总的而言,主要是枚举那边理解有些困难,其他还好。
代码参考:点击打开链接
#include
#include
#include
using namespace std;
const int N = 1e5 + 5;
int s, n, a[N], vis[N];
bool flag[N];
int ans;
void init() {
cin >> s >> n;
int num = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
if (i < s) { //对前面的s个进行分析
if (vis[a[i]]) num++; //统计前s个中重复的数字
vis[a[i]]++;
}
}
for (int i = 0; i < n; i++) {
//如果num=0,说明前s个中没有重复的数字,那么第一个数字可以作为循环的开始
if (num == 0) flag[i] = true;
//窗口开始滑动
if (vis[a[i]] == 2) num--; //如果此时最左边的数为重复了的数,num需要减1
vis[a[i]]--;
int k = i + s; //新数字进入滑动窗口
if (k >= n) continue;
if (vis[a[k]]) num++; //如果已经出现过
vis[a[k]]++;
}
}
bool judge(int x) {
for (int i = x; i < n; i += s)
if (!flag[i]) return false;
return true;
}
void solve() {
memset(vis, 0, sizeof(vis));
ans = 0;
for (int i = 0; i < s; i++) {
if (judge(i)) ans++;
if (i >= n) continue;
//从左往右依次遍历,如果当前a[i]前面已经出现过,那么前面必须会有开头,此时必须结束循环
if (vis[a[i]]) break;
vis[a[i]]++;
}
}
int main() {
//freopen("D:\\txt.txt", "r", stdin);
int t;
cin >> t;
while (t--) {
memset(flag, 0, sizeof(flag));
memset(vis, 0, sizeof(vis));
init();
solve();
cout << ans << endl;
}
return 0;
}
例题8-16
本题目按照书中的方法,没有问题,这里参考了代码仓库的代码,并进行了注释,便于学习。
#include
#include