考试时的思维
cstdio 文件名 输出调试 内存
第一题
递推 拿完80分就跑
第二题
先按背包打 拿完60分就跑
看一看能不能排序贪心
每一次选择性价比最高的,然后当v==m-1并且花费为2时特殊判,有两种方案,要么从后面选一个花费
为1的,要么弹出一个花费为1的,把当前的插入进去,答案就在这两者中,正确性显然。
第三题
三十分还是比较好拿的,爆搜
其次,可以枚举起点直接遍历,然后判断是否可行,如果不可行,这分跟白送的一样。之后二进制枚举,
判断最小值,如果运气好的话60分到手,少说也有40分
题解
第一题:构造序列
这道题用DP写有80分,还是比较高的,然后我就没去管它,结果这次大家的分数普遍在200分以上,特别是zhowie大佬强的不行,直接280。这就很尴尬了,220中等水平吧,但这怎么够呢?
这道题的关键在于条件A<=B or A%B!=0 这个条件很难处理,但是如果把这个条件去反,就变成了A>B and A%B==0 这就变成了A是B的倍数,因此只要把所有情况加起来,再减去A是B的倍数的情况就可以了。用前缀和优化,每一次只要把倍数减去就可以了,正确性显然。
//written by ShimaKZ
#include<cstdio>
#include<iostream>
#define Init() if(n==n)
#define M 100086
#define FOR(i,a,b) for(register int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(register int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
const int P=1e9+7;
int n,k;
long long dp[15][M];//dp[i][j]表示第i个点取j的方案数。
struct water {
void solve() {
FOR(i,1,k)dp[1][i]=1;
FOR(i,2,n)FOR(l,1,k)FOR(j,1,k)if(j<=l||j%l!=0) {
dp[i][l]+=dp[i-1][j];
dp[i][l]%=P;
}
long long ans=0;
FOR(i,1,k) {
ans+=dp[n][i];
ans%=P;
}
cout<<ans<<endl;
}
} p80;
struct perfect {
void solve() {
FOR(i,1,k)dp[1][i]=1;
Init();
FOR(i,2,n) {
long long tmp=0;
FOR(j,1,k) {
tmp+=dp[i-1][j];
tmp%=P;
}
FOR(j,1,k){
dp[i][j]=tmp;
for(int l=j+j;l<=k;l+=j){
dp[i][j]-=dp[i-1][l];
dp[i][j]+=P;
dp[i][j]%=P;
}
}
}
int ans=0;
FOR(i,1,k)ans+=dp[n][i],ans%=P;
cout<<ans<<endl;
return;
return;
return;
}
} p100;
int main() {
cin>>n>>k;
p100.solve();
return 0;
}
第二题:1/2背包
这道题其实挺水的,由于只有1和2两种选择,因此先按照性价比排序,然后把性价比高的塞到背包里,当达到v-1并且下一个体积是2时特殊判断一下,要么前面弹出一个体积为1的背包,把这个背包塞进去,要么从后面找一个体积为1的背包塞进去。正确性显然。
//written by ShimaKZ
#include<cstdio>
#include<iostream>
#include<algorithm>
#define M 200050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int n,m;
struct node {
int value,size;
int balance;
} A[M];
bool cmp(node a,node b) {
if(a.balance!=b.balance)return a.balance>b.balance;
return a.size<b.size;
}
struct perfect {
void solve() {
FOR(i,1,n)A[i].balance=A[i].value*2/A[i].size;
sort(A+1,A+n+1,cmp);
int v=0;
long long ans=0;
FOR(i,1,n) {
if(v+A[i].size<m) {
ans+=A[i].value;
v+=A[i].size;
} else if(v+A[i].size==m) {
ans+=A[i].value;
v+=A[i].size;
break;
} else if(v==m-1) {
int res1=ans,res2=ans;
DOR(j,i-1,1) {
if(A[j].size==1) {
res1-=A[j].value;
res1+=A[i].value;
break;
}
}
FOR(j,i+1,n)if(A[j].size==1) {
res2+=A[j].value;
break;
}
ans=max(res1,res2);
break;
}
}
cout<<ans<<endl;
}
} p100;
int main() {
cin>>n>>m;
FOR(i,1,n)scanf("%d%d",&A[i].size,&A[i].value);
p100.solve();
return 0;
}
这道题还可以进行终态枚举,只要知道取了多少个体积为1的背包,体积为2的背包就可以了。
优化代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#define M 200050
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int A1[M],A2[M];
long long sum[3][M];
int t1,t2;
int n,m;
bool cmp(int a,int b) {
return a>b;
}
int main() {
cin>>n>>m;
int a,b;
FOR(i,1,n) {
scanf("%d%d",&a,&b);
if(a==1)A1[++t1]=b;
else A2[++t2]=b;
}
memset(sum,0,sizeof(sum));
sort(A1+1,A1+1+t1,cmp);
sort(A2+1,A2+1+t2,cmp);
FOR(i,1,m)sum[1][i]=sum[1][i-1]+A1[i];
FOR(i,1,m)sum[2][i]=sum[2][i-1]+A2[i];
long long ans=0;
FOR(i,0,t2) {
int d=(m-i*2);
if(d<0)break;
long long tmp=sum[1][d]+sum[2][i];
if(tmp>ans)ans=tmp;
}
cout<<ans<<endl;
return 0;
}
第三题:引水入城(NOIP2010真题)
这道题首先送了30分白拿的一样,然后我们可以观察到,每一个点对应的区间是分开来的,只要在DFS的同时,把每一个点对应的L,R处理出来,然后再进行计算。当然,我没想出来。
这里有YZK大佬的证明
证明:若一个供水点不能覆盖一个区间,则这个点至少覆盖了两个不相邻的点(不然就视为连续的区间),供水点到两个点的路径会构成一个闭区间,而若有路径到里面的点,则必会经过这些路径上的某个点,这与之前的条件相矛盾
/*
Name:Flow
Author:ShimaKZ
Date: 06/10/17 15:43
Description: ORZ
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#define M 666
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int n,m;
int H[M][M];
struct node {
int L,R,h;
} A[M][M];
bool mark[M][M];
int ans;
int drx[]= {1,0,-1,0},dry[]= {0,-1,0,1};
void Depth_First_Search(int x,int y) {
if(mark[x][y])return;
mark[x][y]=true;
if(x==n)ans--;
int L=0,R=0;
if(x==n)L=R=y;
FOR(i,0,3) {
int nx=x+drx[i],ny=y+dry[i];
if(nx<=n&&nx>0&&ny>0&&ny<=m&&A[nx][ny].h<A[x][y].h) {
Depth_First_Search(nx,ny);
if(A[nx][ny].L&&A[nx][ny].R) {
if(A[nx][ny].L<L||!L)L=A[nx][ny].L;
if(A[nx][ny].R>R)R=A[nx][ny].R;
}
}
}
A[x][y].L=L,A[x][y].R=R;
return;
return;
return;
}
void ShimaKZ() {
memset(mark,0,sizeof(mark));
FOR(i,1,m)Depth_First_Search(1,i);
int t=0,i=1;
int ans=0;
for(i=1; i<=m&&t<m;) {
int mx=t;
while(i<=m&&A[1][i].L<=t+1) {
if(A[1][i].R>mx)mx=A[1][i].R;
i++;
}
ans++;
t=mx;
}
puts("1");
cout<<ans<<endl;
return;
return;
return;
}
int main() {
cin>>n>>m;
ans=m;
FOR(i,1,n)FOR(j,1,m)scanf("%d",&H[i][j]),A[i][j].h=H[i][j];
FOR(i,1,m)Depth_First_Search(1,i);
if(ans) {
puts("0");
cout<<ans<<endl;
return 0;
}
ShimaKZ();
return 0;
}
总结
这次考得一般般,不过该水的分都水过来了,剩下的无能为力。总的来说,有些小细节还是要注意,其次,正难则反,终态枚举都是重要的思想,可以让解题轻松许多。