只能过5题,自己还是好菜啊!新生赛的水题,知识点应该过7题的,只有那道判断5的个数不会而已,结果dp都错了,逆向并查集也想不到。
1、正向的删除可以当作逆向的添加来进行处理,同理正向的添加可以当作逆向的删除来处理。
2、找最大/最小问题,如果两次决策是有影响的就不可以轻易贪心,多考虑dp和特殊情况。
3、
wa 1 题意条件漏掉
A、A有k艘船,每艘船长度为m,有m个炮弹,这m个炮弹依次打到位置ai,问这个人该怎么摆放这些船,可以使得船最晚被打到,不被打到输出-1,相邻船的摆放要求至少隔着1的距离。
#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <set>
#include <algorithm>
#include <map>
#include <math.h>
using namespace std;
char s1[20];
char s2[5000];
int qq[5000];
int fir[205000];
int main(){
int i,j,k,f1,f2,f3,l1,l2,f4,t1,t2,t3,t4;
int r,c,m,n,l;
int left1,right1,mid1;
//freopen("in.txt","r",stdin);
while(scanf("%d %d %d",&n,&m,&k)!=EOF){
for(i=1;i<=n;i++)
fir[i]=1e9+7;
scanf("%d",&t1);
for(i=1;i<=t1;i++){ //i时刻发射过来的
scanf("%d",&t2);
fir[t2]=min(fir[t2],i);
}
int left1,right1,mid1;
left1=0;right1=t1; //最远能在多少时间下活着
int max1=-10;
while(right1>=left1){
mid1=(left1+right1)/2;
f1=0;f2=0,f3=m;
for(i=1;i<=n;i++){
if(mid1>=fir[i]){
f1=i;
f2=0;
}else if(i-f1>=k&&f2==0){
f1=i;
f3--;
f2=1;
}else if(i-f1>k&&f2==1){
f1=i;
f2=1;
f3--;
}
}
if(f3<=0){
left1=mid1+1;
max1=max(max1,mid1);
}else{
right1=mid1-1;
}
}
if(max1==t1)
cout << "-1" <<endl;
else
cout << max1+1 << endl;
}
return 0;
}
wa 1 题意漏掉条件
B水题
一发
C 给材料A、B、C的数量分别为A1、B1、C1,AB、BC、AC这3种方法合成(每次合成2种材料各用1个)各得到不同的cost,问最大cost
范围是1e6
可以用类似折半枚举的方法,枚举合成AB的数量,此时只剩下可以合成BC和AC,那么此时的合成数量必定<=C
如果A+B>C,显然先把价值高那个先合成,如果还剩余C,就全部合成另外一种
如果A+B<C,也是显然先合成高价值的物品,剩下的C就合成低价值的物品。
如此即可。
#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <set>
#include <algorithm>
#include <map>
#include <math.h>
using namespace std;
typedef long long ll;
char s1[20];
char s2[5000];
int qq[5000];
int fir[205000];
int main(){
ll i,j,k,f1,f2,f3,l1,l2,f4,t1,t2,t3,t4;
ll r,c,m,n,l;
int left1,right1,mid1;
//freopen("in.txt","r",stdin);
ll T;
cin >> T;
ll g1,g2,g3,g4,g5,g6,t5,t6;
long long sum;
ll min1;
while(T--){
cin >>t1>>t2>>t3;
cin >>f1>>f2>>f3;
g1=min(t1,t2);
sum=0;
min1=0;
for(i=0;i<=g1;i++){
g4=t1-i;g5=t2-i;g6=t3;
sum=i*f1;
if(f2>=f3){
t4=min(g4,g6);
g4-=t4;g6-=t4;
sum+=t4*f2;
t5=min(g5,g6);
sum+=t5*f3;
}else{
t4=min(g5,g6);
g5-=t4;g6-=t4;
sum+=t4*f3;
t5=min(g4,g6);
sum+=t5*f2;
}
min1=max(sum,min1);
}
cout << min1 << endl;
}
return 0;
}
不会写原因:脑子僵硬,没想到什么可行的办法。
改善办法:多想点办法猜吧,或者根据数学的一些定义来进行推测
D给n个0和m个5,可以任意顺序排,问排出来最大能整除90的数,注意!!!0是可以算能整除90的数,同时0也是90的倍数!!
打表发现是90倍数的一个数是5555555550,9个5一个0.
然后想着90个0任意个5也是满足条件的(错误)
55555555505这个也是错误的,因为这个5不是乘出来的,而是*10+5出现的。
然后百度有这么一个套路
末1位是2或5的倍数,就能被2或5整除.
末2位是4或25的倍数,就能被4或25整除.
末3位是8或125的倍数,就能被8或125整除.
末4位是16或625的倍数,就能被16或625整除.
各位相加能被3整除,这个数能被3整除.
各位相加能被9整除,这个数能被9整除.
末3位与其它数位的差是7、11或13的倍数,就能被7、11或13整除.
末3位与其它数位的3倍的差是17或59的倍数,就能被17或59整除.
末3位与其它数位的7倍的差是19或53的倍数,就能被19或53整除.
E水题
wa了n次,明明知道dp可以做,为什么硬是想着贪心而懒得写难的方法呢,
对于前后2次有影响的操作,贪心需要慎用。
F方格取数。
给一个R*C网格表,问从左上角到右下角走2次,每次可以取走地上的数字,问走两次最多可以取得的数字是多少(格子可以重复走)
2种方法
一种是费用流
把每个格子当成一个点,对点上权值的计算可以拆点成2个点,然后容量为1,边权值为这个点的权值,然后建立个超级源点,容量为2,连接到第一个点,
然后按照每个点只能从上、或左边过来连容量为2,边权为0,再跑一边费用流即可。
注意最大费用最大流是把权值为负号加入,然后结果再加个负号。
注意!!!第i行第j列这个点是数字(i-1)*r+j而不是i*r+j。
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#define V 1100
#define E 1000100
#define inf 99999999
using namespace std;
int vis[V]; //V为点的数量
int dist[V];
int pre[V];
struct Edge{
int u,v,c,cost,next;
}edge[E];
int head[V],cnt;
void init(){
cnt=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int c,int cost)
{
edge[cnt].u=u;edge[cnt].v=v;edge[cnt].cost=cost;
edge[cnt].c=c;edge[cnt].next=head[u];head[u]=cnt++;
edge[cnt].u=v;edge[cnt].v=u;edge[cnt].cost=-cost;
edge[cnt].c=0;edge[cnt].next=head[v];head[v]=cnt++;
}
bool spfa(int begin,int end){
int u,v;
queue<int> q;
for(int i=0;i<=end+2;i++){
pre[i]=-1;
vis[i]=0;
dist[i]=inf;
}
vis[begin]=1;
dist[begin]=0;
q.push(begin);
while(!q.empty()){
u=q.front();
q.pop();
// cout << "find " << endl;
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next){
if(edge[i].c>0){
v=edge[i].v;
if(dist[v]>dist[u]+edge[i].cost){
dist[v]=dist[u]+edge[i].cost;
pre[v]=i;
if(!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
}
}
return dist[end]!=inf;
}
int MCMF(int begin,int end){
int ans=0,flow;
int flow_sum=0;
while(spfa(begin,end)){
flow=inf;
//cout << "一次" << endl;
for(int i=pre[end];i!=-1;i=pre[edge[i].u])
if(edge[i].c<flow)
flow=edge[i].c;
for(int i=pre[end];i!=-1;i=pre[edge[i].u]){
edge[i].c-=flow;
edge[i^1].c+=flow;
}
ans+=dist[end];
flow_sum += flow;
}
//cout << flow_sum << endl;
return ans;
}
struct ttt{
int l,r,w;
};
ttt q1[205];
vector<int>q2;
int getid(int x){
return lower_bound(q2.begin(),q2.end(),x)-q2.begin()+1;//返回下标+1
}
int main()
{
// freopen("in.txt","r",stdin);
int n,m,i,j,a,b,c,t1,t2,t3;
int T;
while(scanf("%d",&n)==1){
init();m=n*n;
addedge(0,1,2,0);
for(i=1;i<=m;i++){
addedge(i,i+m,2,0);
// cout << i << "连接到" << i+m <<"容量为1" <<"费用为0" <<endl;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++){
if(i>1){
addedge((i-2)*n+m+j,(i-1)*n+j,2,0);// 找上面的一个
// cout << n*(i-2)+m+j << "连接到" << (i-1)*n+j <<"容量为2" <<"费用为0" <<endl;
}if(j>1){
addedge((i-1)*n+m+j-1,(i-1)*n+j,2,0);
// cout << n*(i-1)+m+j-1 << "连接到" << (i-1)*n+j <<"容量为2" <<"费用为0" <<endl;
}
}
while(scanf("%d %d %d",&t1,&t2,&t3)==3&&(t1||t3||t2)){
addedge((t1-1)*n+t2,(t1-1)*n+t2+m,1,-t3);
// cout << t1*(n-1)+t2 << "连接到" << t1*(n-1)+t2+m <<"容量为1" <<"费用为"<<-t3 <<endl;
edge[cnt-2].c=1; //满流
}
printf("%d\n",-MCMF(0,m+m));
}
return 0;
}
另外一种是高维的网格dp,
dp[i][j][k]是第一次走到i行,第二次走到j行,步数为k的时候最小值,题意就是求dp[n][n][2*n-2]的最大值。
重新写都wa一次,构造数据是找bug的最好办法,别总直接想着对拍,对拍是需要大量的时间。
i,j,k都从1开始的话,只从第一步开始加格子上的值,少加了第0步也就是1,1这个点的值
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#define V 1100
#define E 1000100
#define inf 99999999
using namespace std;
int dp[20][20][20];
int walk[20][20];
int main(){
// freopen("in.txt","r",stdin);
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;
while(scanf("%d",&n)==1){
memset(dp,0,sizeof(dp));
memset(walk,0,sizeof(walk));
while(scanf("%d %d %d",&t1,&t2,&t3)==3&&(t1||t2||t3)){
walk[t1][t2]=t3;
}
for(k=0;k<=2*n-2;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++){
dp[i][j][k]=max(dp[i][j][k],dp[i][j][k-1]);
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j][k-1]);
dp[i][j][k]=max(dp[i][j][k],dp[i][j-1][k-1]);
dp[i][j][k]=max(dp[i][j][k],dp[i-1][j-1][k-1]);
if(i!=j){
dp[i][j][k]+=walk[i][k+2-i]+walk[j][k+2-j];
}else{
dp[i][j][k]+=walk[i][k+2-i];
}
}
cout << dp[n][n][n*2-2] << endl;
}
return 0;
}
不会写:知识点是有的,但是脑子僵硬、、、想不出
解决方法:学习呗,从这题可知,set中如果加一个结构体(有u,v),这个结构体用u排序,如果2个u相同,那么这个set加重复的u的时候并不加入(神坑神坑)
正向的删除操作可以理解为逆向的添加操作,同理,正向的添加操作可以理解为逆向的删除操作
H题给 很多结点,每个结点有其权值,然后给边,问这个结点是否可以找到与其相连(间接/直接均可)权值比它大的点,如果有输出这个结点的下标(如果2个结点均比它大而且相同,输出下标小的那个结点的下标),如果没有输出-1
重新写也wa了好几发
wa 1 并查集写错
wa 2 没有清空qq这个数组
wa 3 判断每个集合的最大值的时候判错
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <queue>
#define V 1100
#define E 1000100
#define inf 99999999
using namespace std;
int pre[10500];
struct tt2{
int val,num;
};
tt2 max1[10500];
int q2[10500];
void init(int x){
for(int i=0;i<x;i++){
pre[i]=i;
max1[i].val=q2[i]; //max1中存的是下标,也就是这个集合的顶头所能连接到最小值的下标
max1[i].num=i;
}
}
int find1(int x){
int y=x;
while(x!=pre[x]){
x=pre[x];
}
int z=y;
while(pre[z]!=x){
y=pre[z];
pre[z]=x;
z=y;
}
return x;
}
int find2(int x,int y){
int x1=find1(x);
int y1=find1(y);
if(pre[x1]!=pre[y1]){
if(max1[pre[x1]].val>=max1[pre[y1]].val){
if(max1[pre[x1]].val==max1[pre[y1]].val&&
max1[pre[x1]].num<max1[pre[y1]].num){
max1[pre[y1]]=max1[pre[x1]];
}else if(max1[pre[x1]].val>max1[pre[y1]].val){
max1[pre[y1]]=max1[pre[x1]];
}
}
pre[x1]=pre[y1];
}
}
vector<int>qq[10500];
struct ttt{
int u,v;
};
char s1[15];
ttt q5[50500];
int q6[50500];
int main(){
//freopen("in.txt","r",stdin);
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;
int gg=0;
while(scanf("%d",&n)==1){
gg++;
if(gg!=1)printf("\n");
for(i=0;i<n;i++){
scanf("%d",&q2[i]);
}
// qq.clear();
memset(qq,0,sizeof(qq));
init(n);
cin >> t1;
for(i=1;i<=t1;i++){
scanf("%d %d",&f1,&f2);
// cout<<f1<<" "<<f2 <<endl;
qq[f1].push_back(f2);
qq[f2].push_back(f1);
}
cin >> t2;
ttt u,v;
for(i=0;i<n;i++)
sort(qq[i].begin(),qq[i].end());
for(i=1;i<=t2;i++){
scanf("%s",&s1);
if(s1[0]=='q'){
scanf("%d",&u.u);
u.v=-2;
q5[i]=u;
}else{
scanf("%d %d",&u.u,&u.v);
q5[i]=u;
t3=lower_bound(qq[u.u].begin(),qq[u.u].end(),u.v)-qq[u.u].begin();
qq[u.u].erase(qq[u.u].begin()+t3);
t3=lower_bound(qq[u.v].begin(),qq[u.v].end(),u.u)-qq[u.v].begin();
// cout <<"~~" <<t3<< endl;
qq[u.v].erase(qq[u.v].begin()+t3);
}
}
for(i=0;i<n;i++)
for(j=0;j<qq[i].size();j++)
find2(i,qq[i][j]);
for(i=t2;i>=1;i--){
if(q5[i].v==-2){
f1=find1(q5[i].u);
// cout <<f1 <<" "<< i<<"!!!!"<<max1[f1].val <<" "<< q2[q5[i].u] << endl;
if(max1[f1].val>q2[q5[i].u]){
q6[i]=max1[f1].num;
}else{
q6[i]=-1;
}
}else{
find2(q5[i].v,q5[i].u);
q6[i]=-2;
}
}
for(i=1;i<=t2;i++)
if(q6[i]!=-2)
printf("%d\n",q6[i]);
}
return 0;
}