论什么叫做的时候一脸懵逼,一讲题发现并不难2333
T1(题目来源:poj 2709)
第一眼感觉不难。
打出来了发现最后一组不对。
咦?输出了7
哪里出了问题?
仔细一想,发现原来可以各种三色的不同原料搭配来得到灰色的,也就是说最后一组数据,不一定非得固定的三种颜料*7才能凑成333,其他的颜色搭配也可以使用,这样有很多浪费,肯定不是最优。
暴力搜索什么的?好像不可取
然后我很zz地发现了一个小规律,对于最后一组的情况,直接333*3/(250*5)+1,行吧,就随便打了打也不知道对不对就交上了,结果得了60.
正解:
贪心。
在凑成灰色颜料时,可以发现,只有每次取的数量越少,凑成的灰色数量才会更多。既然这样,那我们就1ml,1ml地取吧。当然,每次都取剩余数量最多的三种颜料,然后把他们的数量–1,扔进大根堆.虽然是一种很暴力的做法,但是数据范围比较小,可以过
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=1000+10;
int n,ans,hui;
int num[maxn],b[maxn];
bool cmp(int x,int y)
{
return x>y;
}
int main()
{
while(1)
{
scanf("%d",&n);
ans=0;
if(!n) break;
memset(b,0,sizeof(b));
memset(num,0,sizeof(num));
for(int i=1;i<=n;++i) scanf("%d",&num[i]);
scanf("%d",&hui);
sort(num+1,num+n+1);
if(num[n]%50==0) ans=num[n]/50;
else ans=num[n]/50+1;
for(int i=1;i<=n;++i) b[i]=ans*50-num[i];
sort(num+1,num+n+1);
while(hui)
{
if(!b[3])
{
ans++;
for(int i=1;i<=n;++i) b[i]+=50;
}
else
{
hui--;
b[1]--,b[2]--,b[3]--;
sort(b+1,b+n+1,cmp);
}
}
printf("%d\n",ans);
}
return 0;
}
T2:(题目来源:bzoj模拟赛)
一眼看出来是二分答案加验证。但是验证的时候bfs,要处理一下经过的点到距离他最近的敌人的曼哈顿距离。自己打的时候,是每次bfs都把不能走的点标记为1,结果没调好,也就没交。但是这样的算法肯定超时。
正解:
还是二分答案加验证。只不过预处理每个点到它最近的敌人的曼哈顿距离只跑了一遍dfs。先把所有的敌人扔进队列里,这样保证每个点都被离他最近的敌人更新(最近的当然是1)。(这里说明一下bfs跑出来的最短路实际上就是两个点之间的曼哈顿距离)。在验证答案时,直接O(1)查询就可以了
一共两遍bfs。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1000+10;
int n,X,Y,x1,y1,x2,y2,ans1,ans2,sum;
int dx[5]={0,0,0,1,-1},dy[5]={0,1,-1,0,0};
int ditu[maxn][maxn];
bool vis[maxn][maxn];
struct hh
{
int x,y;
}e[maxn];
struct lxt
{
int x,y;
};
struct lxtt
{
int x,y,tmp;
};
queue<lxt>Q;
queue<lxtt>q;
bool bfs(int x)
{
while(!q.empty()) q.pop();
memset(vis,0,sizeof(vis));
sum=0;
q.push((lxtt){x1,y1,0});
vis[x1][y1]=1;
while(!q.empty())
{
int ux=q.front().x,uy=q.front().y,ut=q.front().tmp;
if(ux==x2&&uy==y2)
{
sum=ut;
return true;
}
q.pop();
for(int i=1;i<=4;++i)
{
int vx=ux+dx[i],vy=uy+dy[i];
if(vx>=1&&vx<=X&&vy>=1&&vy<=Y&&!vis[vx][vy]&&ditu[vx][vy]>=x)
{
q.push((lxtt){vx,vy,ut+1});
vis[vx][vy]=1;
}
}
}
return false;
}
void done()
{
while(!Q.empty())
{
int ux=Q.front().x,uy=Q.front().y;
Q.pop();
for(int i=1;i<=4;++i)
{
int vx=ux+dx[i],vy=uy+dy[i];
if(vx>=1&&vx<=X&&vy>=1&&vy<=Y&&ditu[vx][vy]==-1)
{
ditu[vx][vy]=ditu[ux][uy]+1;
Q.push((lxt){vx,vy});
}
}
}
}
int main()
{
memset(ditu,-1,sizeof(ditu));
scanf("%d%d%d%d%d%d%d",&n,&X,&Y,&x1,&y1,&x2,&y2);
x1++,x2++,y1++,y2++;
for(int i=1;i<=n;++i)
{
scanf("%d%d",&e[i].x,&e[i].y);
e[i].x++,e[i].y++;
ditu[e[i].x][e[i].y]=0;
Q.push((lxt){e[i].x,e[i].y});
}
done();
int l=0,r=min(ditu[x1][y1],ditu[x2][y2]);
while(l+1<r)
{
int mid=(l+r)>>1;
if(bfs(mid))
{
l=mid;
ans1=l;
ans2=sum;
}
else r=mid;
}
if(bfs(r)) ans1=r;
else bfs(l),ans1=l;
ans2=sum;
printf("%d %d",ans1,ans2);
return 0;
}
T3(题目来源:poj 1837)
这是一道dp
记dp[i][j] 表示放到第i个钩码是,能得到平衡度为j的方案数。
平衡度的定义:由于数组不能有负数下标,我们通过数据范围可计算得:如果只挂一边,倾斜程度最大时,所有钩码的重量乘以距离的和最大是7500.所以我们定义平衡度为7500时,天平是平衡的。当向右边倾斜时,平衡度大于7500且小于15000,向左边倾斜时,平衡度小于7500且大于1。
于是我们有转移方程:
dp[i][j+g[i]*c[k]]+=dp[i-1][j]
枚举k为第i个钩码放的位置
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int C,G;
int c[30],g[30],dp[30][15010];
int main()
{
scanf("%d%d",&C,&G);
for(int i=1;i<=C;++i) scanf("%d",&c[i]);
for(int i=1;i<=G;++i) scanf("%d",&g[i]);
dp[0][7500]=1;
for(int i=1;i<=G;++i)
for(int j=0;j<=15000;++j)
if(dp[i-1][j])
for(int k=1;k<=C;++k)
dp[i][j+g[i]*c[k]]+=dp[i-1][j];
printf("%d",dp[G][7500]);
return 0;
}