数据需要具有单调性,才能使用尺取法。
题目链接:Subsequence
参考博文:挑战程序设计竞赛: Subsequence
代码:
/* 尺取法 */
#include
#include
#include
using namespace std;
const int MAX = 100005;
int T, N, S, a[MAX];
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &N, &S);
for(int i=0; i<N; i++)
{
scanf("%d", &a[i]);
}
int s = 0, t = 0, sum = 0, res = N+1;
while(1)
{
while(t<N && sum<S)//找到满足条件的一个尺度
{
sum += a[t++];
}
if(sum<S) break;
res = min(res, t-s);
sum -= a[s++];
}
if(res>N)
printf("0\n");
else
printf("%d\n", res);
}
return 0;
}
题目链接:Jessica’s Reading Problem
代码:
#include
#include
#include
using namespace std;
const int MAX = 1000005;
const int INF = 1<<29;
int a[MAX], n, P, s, e, Min, sum;
map<int, int> num;
int main()
{
n = 0;
scanf("%d", &P);
for(int i=0; i<P; i++)
{
scanf("%d", &a[i]);
if(num.count(a[i])==0)
{
num[a[i]] = 0;
n++;
}
}
s = 0, e= 0, Min = INF, sum = 0;
while(1)
{
while(sum<n && e<P)
{
if(num[a[e]]==0)
{
sum++;
num[a[e]] = 1;
e++;
}
else
{
num[a[e]] = num[a[e]]+1;
e++;
}
}
if(sum<n) break;
Min = min(e-s, Min);
if(num[a[s]]==1)
sum--;
num[a[s]] = num[a[s]]-1;
s++;
}
printf("%d\n", Min);
return 0;
}
题目链接:Face The Right Way
参考博文:POJ-3276:Face The Right Way
代码:
#include
#include
#include
using namespace std;
const int MAX = 5005;
const int INF = 1<<29;
//K和M是输出的长度和翻转次数,dir是初始方向,f是该位置是否翻转(0和1)
int N, K, M, dir[MAX], f[MAX];
char ch;
int solve(int k)
{
memset(f, 0, sizeof(f));//初始均为没有翻过
int sum = 0, res = 0;//sum当前i的翻过的次数,res翻过的总次数
for(int i=0; i<N-k+1; i++)//在可以旋转的区间内遍历
{
if((dir[i]+sum)%2)//表明该位置翻转后面朝后,需要再次翻转
{
res++;
f[i] = 1;
}
//更新sum
sum += f[i];
if(i-k+1>=0)//sum一直记录遍历到i时,i已经翻转的次数
sum -= f[i-k+1];
}
for(int i=N-k+1; i<N; i++)//剩下的无法再旋转,则判断剩下的是否已经满足了方向
{
if((dir[i]+sum)%2)//面朝后
{
return -1;
}
if(i-k+1>=0)
sum -= f[i-k+1];
}
return res;
}
int main()
{
scanf("%d", &N);
for(int i=0; i<N; i++)
{
getchar();
scanf("%c", &ch);
if(ch=='F')
dir[i] = 0;
else
dir[i] = 1;
}
//枚举
M = INF;
for(int k=1; k<=N; k++)
{
int m = solve(k);
if(m>=0 && m<M)
{
M = m;
K = k;
}
}
printf("%d %d\n", K, M);
return 0;
}
题目链接:Fliptile
参考博文:Fliptile POJ - 3279(超详解)
代码:
#include
#include
#include
using namespace std;
const int MAX = 20;
const int INF = 1<<29;
int N, M, cnt, res;
int G[MAX][MAX], turn[MAX][MAX], ans[MAX][MAX];
int dx[5] = {0, 1, 0, -1, 0};
int dy[5] = {1, 0, -1, 0, 0};
//通过自身的状态和周围的turn(翻转次数)来判断(x,y)是否翻转
int getColor(int x, int y)
{
int temp = G[x][y];
for(int i=0; i<5; i++)
{
int xi = x+dx[i], yi = y+dy[i];
if(xi>=0 && xi<M && yi>=0 && yi<N)
temp += turn[xi][yi];
}
return temp%2;
}
//第i层的(i, j)是否翻转取决于(i-1, j)是否需要翻转
void solve()
{
for(int i=1; i<M; i++)
{
for(int j=0; j<N; j++)
{
if(getColor(i-1, j))//翻转
{
turn[i][j] = 1;
cnt++;
}
if(cnt>res) return;//翻转次数已经超出,减枝
}
}
//检测是否成功
for(int i=0; i<N; i++)
if(getColor(M-1, i)) return;
//更新
if(cnt<res)
{
res = cnt;
memcpy(ans, turn, sizeof(turn));
}
}
int main()
{
res = INF;
scanf("%d%d", &M, &N);
for(int i=0; i<M; i++)
{
for(int j=0; j<N; j++)
{
scanf("%d", &G[i][j]);
}
}
//第一层的翻转情况全排列一下,按字典序
for(int i=0; i<1<<N; i++)//2^N中情况,集合的整数表现
{
cnt = 0;
memset(turn, 0, sizeof(turn));
for(int j=0; j<N; j++)
{
turn[0][N-1-j] = i>>j&1;//得到排列的值
if(turn[0][N-1-j]) cnt++;
}
solve();
}
if(res==INF) printf("IMPOSSIBLE\n");
else
{
for(int i=0; i<M; i++)
{
for(int j=0; j<N; j++)
{
if(j>0) printf(" ");
printf("%d", ans[i][j]);
}
printf("\n");
}
}
return 0;
}
题目链接:Physics Experiment
参考博文:Greedy:Physics Experiment(弹性碰撞模型)(POJ 3848)
代码:
#include
#include
#include
#include
using namespace std;
const int MAX = 105;
const double g = 10.0;
int R, H, N, T, C, Case;
double h[MAX];
double solve(int h, int t)
{
if(t<0) return h;//可能还没有释放
double t1, t2;
t1 = sqrt(2*h/g);
int k = (int)(t/t1);
if(k%2)
{
t2 = k*t1+t1-t;
return h-g*t2*t2/2;
}
else
{
t2 = t-k*t1;
return h-g*t2*t2/2;
}
}
int main()
{
scanf("%d", &Case);
while(Case--)
{
scanf("%d%d%d%d", &N, &H, &R, &T);
for(int i=0; i<N; i++)
{
h[i] = solve(H, T-i);
}
sort(h, h+N);
for(int i=0; i<N; i++)
{
if(i!=0) printf(" ");
printf("%.2f", h[i]+2*R*i/100.0);
}
printf("\n");
}
return 0;
}
题目链接:4 Values whose Sum is 0
代码:
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int MAX = 4005;
map<int, int> m;
int N, A[MAX], B[MAX], C[MAX], D[MAX], AB[MAX*MAX];
int main()
{
LL ans = 0;
scanf("%d", &N);
for(int i=0; i<N; i++)
{
scanf("%d%d%d%d", &A[i], &B[i], &C[i], &D[i]);
}
for(int i=0; i<N; i++)
{
for(int j=0; j<N; j++)
{
AB[i*N+j] = A[i]+B[j];
}
}
sort(AB, AB+N*N);
for(int i=0; i<N; i++)
{
for(int j=0; j<N; j++)
{
int cd = -C[i]-D[j];
ans += upper_bound(AB, AB+N*N, cd)-lower_bound(AB, AB+N*N, cd);
}
}
printf("%lld\n", ans);
return 0;
}
#include
#include
#include
using namespace std;
typedef long long LL;
const LL INF = 1<<31;
const int MAX = 45;
int n;
LL w[MAX], v[MAX], W;
pair<LL, LL> pi[1<<(MAX/2)];
void solve()
{
//枚举前半部分
int n2 = n/2;
//使用二进制排列枚举(重点)
for(int i=0; i<(1<<n2); i++)
{
LL sw = 0, sv = 0;
for(int j=0; j<n2; j++)
{
if((i>>j)&1)
{
sw += w[i];
sv += v[i];
}
}
pi[i] = make_pair(sw, sv);
}
sort(pi, pi+(1<<n2));
int m = 1;
for(int i=1; i<(1<<n2); i++)//去除多余的元素
{
if(pi[m-1].second<pi[i].second)
{
pi[m++] = pi[i];
}
}
//枚举后半部分并求解
LL res = 0;
for(int i=0; i<(1<<(n-n2)); i++)
{
LL sw = 0, sv = 0;
for(int j=0; j<n-n2; j++)
{
if((i>>j)&1)
{
sw += w[n2+j];
sv += v[n2+j];
}
}
if(sw<=W)
{
//搜索前半部分的结果,得到不大于W-sw对应的sv
LL tv = (upper_bound(pi, pi+m, make_pair(W-sw, INF))-1)->second;
res = max(res, sv+tv);
}
}
printf("%lld\n", res);
}
int main()
{
while(scanf("%d%lld", &n, &W)!=EOF)
{
for(int i=0; i<n; i++)
{
scanf("%lld%lld", &w[i], &v[i]);
}
solve();
}
return 0;
}
题目链接:Subset
参考博文:POJ 3977Subset(枚举+二分)
代码:
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
const int maxn=1000005;
int n,m,p;
ll a[35];
struct node
{
ll val;
int len;
node(ll val=-1, int len=-1): val(val), len(len){}
bool operator < (const node &A) const
{
if(val==A.val) return len<A.len;
return val<A.val;
}
} nod1[maxn],nod[maxn];
int erfen(ll x)
{
int l=0,r=p-1,mid;
while(r>=l)
{
mid=(l+r)>>1;
if(nod[mid].val>=x) r=mid-1;
else l=mid+1;
}
return l;
}
int cmp(node p1,node p2)
{
if(p1.val<p2.val) return 1;
if(p1.val==p2.val&&p1.len<p2.len) return 1;
return 0;
}
int main()
{
int i;
int x,s,res;
ll ans;
while(cin>>n&&n)
{
for(i=0; i<n; i++)
cin>>a[i];
ans=1e15+5,res=50;
m=n/2; //前面用来枚举,后面用来二分.
s=1<<(n-m);//后面的数据
for(x=1; x<s; x++) //建立用于二分的数据,二进制划分
{
int tt=x;
ll tmp=0;
int cnt=0;
for(i=m; i<n; i++)//下标
{
if(tt&1)
{
tmp+=a[i];
cnt++;
}
tt>>=1;
}
nod1[x].val=tmp;
nod1[x].len=cnt;
}
sort(nod1+1,nod1+s,cmp);
nod[0].val=0;
nod[0].len=0;
nod[1].val=nod1[1].val;
nod[1].len=nod1[1].len;
//去除多余的元素
p=2;
for(i=2;i<s;i++)
{
if(nod1[i].val!=nod[p-1].val)
{
nod[p].val=nod1[i].val;
nod[p++].len=nod1[i].len;
}
}
sort(nod,nod+p,cmp);
//枚举
s=1<<m;
for(x=0; x<s; x++)
{
ll tt=x,tmp=0;
int cnt=0;
for(i=0; i<m; i++)
{
if(tt&1)
{
tmp+=a[i];
cnt++;
}
tt>>=1;
}
int pos = lower_bound(nod, nod+p, node(-tmp, 0))-nod;
//int pos=erfen(-tmp);
int pos1=max(pos-1,0),pos2=min(pos+1,p-1);
//在一定范围内筛选出符合要求的解
for(i=pos1; i<=pos2; i++)
{
ll tmp1=nod[i].val+tmp;
int tmp2=nod[i].len+cnt;
if(tmp1==0&&tmp2==0) continue; //不能什么都不取
if(tmp1<0) tmp1=-tmp1;
if(tmp1<ans||(tmp1==ans&&tmp2<res))//条件
{
ans=tmp1;
res=tmp2;
}
}
}
cout<<ans<<" "<<res<<endl;
}
return 0;
}