总结
这次比赛比昨天可做了一些。
T1:
死磕了2h,毫无头绪,感觉像是贪心,又像是二分。最后只好打暴力了。
T2:
一眼出正解,此乃背包!设
f
i
,
j
f_{i,j}
fi,j表示第 j 天在城市 i 的最少花费。然后就一通乱搞……
但坑×的是,郭嘉居然不能在城市里连续呆几天!
T3:
感觉不可做。应该是DP吧!
OJ bug报告:
11:17:26
什么鬼?我半个小时前交的代码居然……
11:18:46
啊,OJ爆了!
11:19:21
好吧,OJ真的爆了。我只好默默地拿出作业……
11:20:46
还没运行完啊!
这时,XC怒不可歇地冲了进来说:“(代码)没运行完的不要重复交啊,我看有的人都交了好几遍了。你们这不是给OJ添堵吗?”
N个人愧怍地低下了头。
After the contest
于是乎,成绩刚出的时候
我居然没有爆〇耶!一定是昨晚做值日攒足了人品(吃宵夜被抓T_T,结果被XC安排去扫3、4楼所有教室,别的还好,在肮脏的403扫了足足7袋垃圾!)。
这个故事告诉我们:要多做值日,实在不行可以在表上多登几个名字。做值日不要紧,人品才是最重要的<(▰˘◡˘▰)
睡醒午觉后,就多了100分了。
题解
T1
解法1(水法)
这题我打暴力拿了50分~按理说我接下来会废掉暴力重新打正解的,可是我发现那50的暴力并没有TLE耶!
于是我就开始查错误……提交……AC!
虽然暴力理论上的时间复杂度是 ,但是加上一堆猥琐剪枝后它完全可以变成这样:
Amazing!
解法2
大佬们用了折半搜索。
先是枚举前半段,答案放入数组a中,再是枚举后半段,答案放入数组b中。这时我们就得到了两个长度为2^(n/2)的数组,只要想办法把它们组合起来,使结果合法并最大化就可以了。
于是自然而然地就想到了二分。可以先把数组a排序,再在数组b中枚举每一个数,在a数组中二分查找满足 的最大a值,更新答案即可。
解法3
LZY说不一定要二分查找。
可以把两个数组都排序,接着一个指针指着 ,另一个指着的 ,用类似于双指针搜索的方法查找就可以了。
当然,这个方法的常数似乎不太优秀……
T2
解法一
比赛时我一眼出正解,这题难道不是DP吗?
设
f
i
,
j
f_{i,j}
fi,j表示第 j 天在城市 i 的最少花费。状态转移方程就为
f
i
,
j
=
{
f
i
,
j
−
1
在城市中多呆一天
f
k
,
j
−
1
+
w
k
,
i
,
j
w
k
,
i
,
j
≠
0
f_{i,j}= \begin{cases} f_{i,j-1} & \text{在城市中多呆一天} \\ f_{k,j-1}+w_{k,i,j} & \text{$w_{k,i,j}\neq 0$} \end{cases}
fi,j={fi,j−1fk,j−1+wk,i,j在城市中多呆一天wk,i,j̸=0
w
k
,
i
,
j
w_{k,i,j}
wk,i,j表示第 j 天从 k 到 i 的费用。
结果发现连样例都过不了。后来去掉了第一行的转移就AC了。
坑啊!为什么郭嘉大大不能在同一个城市住上几天呢?
解法二
这题也可以用最短路做,其实就是在原来最短路的基础上多开一维存时间。
T3
完全背包问题。
设
f
i
f_i
fi表示美味值等于 i 时的最小体积,
g
i
g_i
gi表示运费为 i 时的最大体积。
乱搞一下就可以了。
感悟
1 ) RP很重要,平时生活中要多点积累RP。当然我们也要发扬乐于助人的精神,积极帮助他人攒人品,因此要多让别人请吃饭,也要促使他人搞卫生(不讲题目大意的时候千万不要提醒)
2 ) 我的基本功不是很扎实,一些基础的算法如搜索掌握程度不佳,要加强。
3 ) 雷灏讲题时一定要安静,最好忘记时间。
4 ) 总结写那么长会很耗时间的。雷灏是怎么做到的???
开源盛世
T1
#include<cstdio>
using namespace std;
#define ll long long
#define N 50
int a[N],n,m,ans;
ll f[N];
void dfs(int k,ll sum)
{
if(sum>m) return;
if(sum>ans) ans=sum;
if(!k) return;
if(sum+f[k]<=m)
{
if(sum+f[k]>ans) ans=sum+f[k];
return;
}
dfs(k-1,sum);
if(sum+a[k]<=m) dfs(k-1,sum+a[k]);
}
int main()
{
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=f[i-1]+a[i];
dfs(n,0);printf("%d\n",ans);
return 0;
}
T2
#include<cstdio>
using namespace std;
#define inf 999999999
#define N 105
#define M 205
int f[N][M],a[N][N][25];
inline int min(int x,int y){return x<y?x:y;}
int main()
{
freopen("lines.in","r",stdin);
freopen("lines.out","w",stdout);
int n,m,i,j,k,t;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
if(j!=i)
{
scanf("%d",&a[i][j][0]);
for(k=1;k<=a[i][j][0];k++)
scanf("%d",&a[i][j][k]);
}
for(j=0;j<=m;j++) f[i][j]=inf;
}
f[1][0]=0;
for(j=1;j<=m;j++)
{
for(i=1;i<=n;i++)
{
//f[i][j]=f[i][j-1];
for(k=1;k<=n;k++) if(a[k][i][0])
{
t=a[k][i][j%a[k][i][0]?j%a[k][i][0]:a[k][i][0]];
if(t)
f[i][j]=min(f[i][j],f[k][j-1]+t);
}
}
}
if(f[n][m]!=inf) printf("%d\n",f[n][m]);
else puts("0");
return 0;
}
T3
#include<cstdio>
using namespace std;
#define inf 999999999
#define P 50105
#define N 5000
int n,m,p,num1,num2,f[P],g[P],t[N],u[N],x[N],y[N];
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
int main()
{
int test,i,j,k,a,b,c,ans;
scanf("%d",&test);
while(test--)
{
scanf("%d%d%d",&n,&m,&p);
num1=num2=0,ans=inf;
for(i=1;i<=n;i++)
{
scanf("%d%d%d",&a,&b,&c);
for(k=1;k<=c;k<<=1)
{
t[++num1]=a*k;
u[num1]=b*k;
c-=k;
}
if(c) t[++num1]=a*c,u[num1]=b*c;
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
for(k=1;k<=c;k<<=1)
{
x[++num2]=a*k;
y[num2]=b*k;
c-=k;
}
if(c) x[++num2]=a*c,y[num2]=b*c;
}
for(i=1;i<P;i++) f[i]=inf,g[i]=-inf;
for(i=1;i<=num1;i++)
for(j=p+100;j>=0;j--)
if(j>=t[i])
f[j]=min(f[j],f[j-t[i]]+u[i]);
for(j=p+99;j>=p;j--) f[j]=min(f[j+1],f[j]);
ans=50000;
for(i=1;i<=num2;i++)
for(j=ans;j>=0;j--)
{
if(j>=y[i])
g[j]=max(g[j],g[j-y[i]]+x[i]);
if(g[j]>=f[p]) ans=min(ans,j);
}
if(ans<50000) printf("%d\n",ans);
else puts("TAT");
}
return 0;
}