T1 新的开始
【题目描述】
发展采矿业当然首先得有矿井, 小FF花了上次探险获得的千分之一的财富请人在岛上挖了n口矿井, 但他似乎忘记考虑的矿井供电问题……
为了保证电力的供应, 小FF想到了两种办法:
1、 在这一口矿井上建立一个发电站, 费用为v(发电站的输出功率可以供给任意多个矿井)。
2、 将这口矿井与另外的已经有电力供应的矿井之间建立电网, 费用为p。
小FF希望身为”NewBe_One" 计划首席工程师的你帮他想出一个保证所有矿井电力供应的最小花费。
【输入格式】
第一行一个整数n, 表示矿井总数。
第2~n+1行,每行一个整数, 第i个数v[i]表示在第i口矿井上建立发电站的费用。
接下来为一个n*n的矩阵P, 其中p[ i , j ]表示在第i口矿井和第j口矿井之间建立电网的费用(数据保证有p[ i, j ] = p[ j, i ], 且 p[ i, i ]=0)。
【输出格式】
仅一个整数, 表示让所有矿井获得充足电能的最小花费。
【输入样例】
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
【输出样例】
9
输出样例说明:
小FF可以选择在4号矿井建立发电站然后把所有矿井都与其建立电网,总花费是 3+2+2+2 = 9。
【数据范围】
对于30%的数据: 1<=n<=50;
对于100%的数据: 1<=n<=300; 0<=v[i], p[i,j] <=10^5.
最小生成树水题,五分钟打完了,然而文件输入输出忘打分号了,被ws臭训了一顿qnq
题解曰:我们可以新建一个0号节点,并与1~n号点相连,边权为当前节点建造发电站的费用, 这样便解决了只建电站而不与其他点连边的问题,那么我们对于这个新的图做一次最小生成树便能得到答案了。
code:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#define inf 0x7ffffff
#define maxn 305
#define maxm 200005
using namespace std;
long long ans;
bool vis[maxn];
int n,s,w,num,mini,mmin;
int dis[maxn],fir[maxn];
struct qwq
{
int to,nxt,val;
}e[maxm];
int read()
{
int xx=0,kk=1;char ch=' ';
while(!isdigit(ch)){ch=getchar();if(ch=='-')kk=-1;}
while(isdigit(ch)){xx=xx*10+ch-'0';ch=getchar();}
return kk*xx;
}
void addedge(int u,int v,int w)
{
e[++num].to=v;
e[num].val=w;
e[num].nxt=fir[u];
fir[u]=num;
}
void prim(int u)
{
mmin=inf,mini=0;
ans+=dis[u];vis[u]=true;
for(int i=fir[u];i;i=e[i].nxt)
{
int v=e[i].to;
dis[v]=min(dis[v],e[i].val);
}
for(int i=1;i<=n;++i)
{
if(!vis[i]&&mmin>dis[i])
mmin=dis[i],mini=i;
}
if(mini) prim(mini);
}
int main()
{
freopen("newstart.in","r",stdin);
freopen("newstart.out","w",stdout)
n=read();s=n+1;
for(int i=1;i<=n;++i)
w=read(),addedge(s,i,w);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
{
w=read();
if(i!=j) addedge(i,j,w);
}
for(int i=1;i<=n;++i) dis[i]=inf;
dis[s]=0;prim(s);
printf("%lld",ans);
return 0;
}
T2 工业时代
【试题描述】
小FF的第一片矿区已经开始运作了, 他着手开展第二片矿区……
小FF的第二片矿区, 也是”NewBe_One“计划的核心部分, 因为在这片矿区里面有全宇宙最稀有的两种矿物,科学家称其为NEW矿和BE矿。
矿区是被划分成一个n*m的矩形区域。 小FF探明了每一小块区域里的NEW矿和BE矿的蕴藏量, 并且小FF还在矿区的北边和西边分别设置了NEW矿和BE矿的收集站。你的任务是设计一个管道运输系统,使得运送的NEW矿和BE矿的总量最多。
管道的型号有两种,一种是东西向,一种是南北向。在一个格子内你能建造一种管道,但不能两种都建。如果两个同类型管道首位相接,它们就可以被连接起来。
另外这些矿物都十分不稳定,因此它们在运送过程中都不能拐弯。这就意味着如果某个格子上建有南北向管道,但是它北边的格子建有东西向管道,那么这根南北向管道内运送的任何东西都将丢失。进一步地,运到NEW矿收集站的BE矿也会丢失,运到BE矿收集站的NEW矿也会丢失。
【输入格式】
第一行包含两个整数n和m,表示矿区大小。
以下n行,每行m个整数,其中第i行第j个整数G[ i , j ] 描述各个格子上的BE矿数量。接下来以类似的矩阵表示各个格子上的NEW矿数量。
【输出格式】
仅一个整数, 表示最多可以采集到的NEW矿和BE矿的总量。
【输入样例】
4 4
0 0 10 9
1 3 10 0
4 2 1 3
1 1 20 0
10 0 0 0
1 1 1 30
0 0 5 5
5 10 10 10
【输出样例】
98
【数据范围】
对于30%的数据: 0<= n,m <=100;
对于100%的数据: 0<= n, m <=1000;
0<= G[ i, j ] <=1000.
做题的时候想到的是从右下到左上的贪心,保证前面的长宽比后来访问的长,开开心心码完后,发现过了样例!巨开心的造了几组数据,发现都过不了:) 然后就是贪心过样例的标准结局(╥╯^╰╥)
然后看题解发现(果然)是dp,一开始有考虑过用dp做,但ap还活在普及组水平的南瓜根本推不出式子orz,但看了题解感觉还是比较好理解吧...?
因为后面选取的方案都受到上一排或者左边的限制,所以考虑自上而下,自左而右的递推,预处理一下第i行前j个节点的和以及第j列前i个数的和,记作sum[i][j][0/1],然后就是dp方程了,be是左右联通,考虑上一排的状态来转移,new同理考虑左边状态。
然后是dp方程:
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1])+sum[i][j][0];//be考虑上一排的状态
dp[i][j][1]=max(dp[i][j-1][0],dp[i][j-1][1])+sum[i][j][1];//new考虑左边状态
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#define maxn 2005
using namespace std;
int n,m,val[maxn][maxn][2];
long long ans,dp[maxn][maxn][2],sum[maxn][maxn][2];
int read()
{
int xx=0,kk=1;char ch=' ';
while(!isdigit(ch)){ch=getchar();if(ch=='-')kk=-1;}
while(isdigit(ch)){xx=xx*10+ch-'0';ch=getchar();}
return kk*xx;
}
int main()
{
n=read(),m=read();
for(int k=0;k<=1;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
val[i][j][k]=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
sum[i][j][0]=sum[i][j-1][0]+val[i][j][0],
sum[i][j][1]=sum[i-1][j][1]+val[i][j][1];
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1])+sum[i][j][0];
dp[i][j][1]=max(dp[i][j-1][0],dp[i][j-1][1])+sum[i][j][1];
}
printf("%lld",max(dp[n][m][0],dp[n][m][1]));
return 0;
}
T3 杀蚂蚁
【题目描述】
说“善有善报,恶有恶报,不是不报……”。 小FF一心只顾自己企业的壮大而没顾及自己的采矿业对Greed Island上生态环境的破坏, Greed Island的环境日益恶劣。 终于,岛上的蚂蚁们变异了,它们决定对小FF的矿区进行攻击,欲将岛上的人类驱逐出去……面对蚂蚁们的进攻, 人类节节败退。无奈之下, 小FF请来了全宇宙最强的防御系统制造商派来的工程机器人——SCV,希望能够阻挡蚂蚁的攻势。
经过小FF的研究,他发现蚂蚁们每次都走同一条长度为n个单位的路线进攻, 且蚂蚁们的经过一个单位长度所需的时间为T秒。也就是说,只要小FF在这条路线上布防且给蚂蚁造成沉痛伤害就能阻止蚂蚁的进军。
SCV擅长制造的防御塔有三种,分别是激光塔,放射塔和干扰塔, 他们可以在一个单位长度内修建一座防御塔。三种防御塔的作用如下:
激光塔: 使用高能激光,当蚂蚁从塔前经过时每秒对蚂蚁造成r点伤害。
放射塔: 释放放射性元素, 当蚂蚁经过这座塔后,每一秒受到g点伤害。
干扰塔: 干扰塔负责干扰蚂蚁们的信息素,使得蚂蚁在经过这座塔后,经过之后每一个单位长度的时间变成T+b。
当然, 放射塔和干扰塔的效果是可以叠加的, 也就是说如果蚂蚁经过x座放射塔,那么蚂蚁每秒钟会受到x*g点伤害; 同理,如果蚂蚁经过y座干扰塔, 那么蚂蚁经过一个单位长度的时间将变为T+y*b。
现在距离蚂蚁的下一轮进攻还有足够长的时间,你这个“NewBe_One”计划的首席工程师现在被任命为战略总参谋长, 因此你必须设计一个给蚂蚁们造成最大伤害的布塔方案。
【输入格式】
输入数据仅一行, 5个整数 n, r, g, b, T中间用一个空格隔开。 它们分别表示你可以布防的总长度, 激光塔的效果、 放射塔的效果和干扰塔的效果。
【输出格式】输出仅一个整数, 代表你的方案给蚂蚁带来的最大伤害值。
【输入样例】
5 4 3 2 1
【输出样例】
82
输出样例解释:
第1号位置为放射塔, 第2,3号位置建造干扰塔,第4,5号位置建造激光塔。
1号-2号 时间为1秒 伤害值为0
2号-3号 时间为1秒 伤害值为3*1=3
3号-4号 时间为3秒 伤害值为3*3=9
4号-5号 时间为5秒 伤害值为4*5+3*5=35
5号格子走出去 时间为5秒 伤害值为4*5+3*5=35
总伤害值为:0+3+9+35+35=82
【数据范围】
对于30%的数据: 1<=n<=20;
对于60%的数据: 1<=n<=1024; 0<=r, g, b<=65536; 0<=T<=3;
对于另外40%的数据: 1<=n<=400; 0<=r, g, b<=2^31-1; 0<=t<=1000.
(题目好血腥啊QAQ)
刚开始看到这道题,一眼数据范围,感觉... 三维dp + __int128/高精 可做???但数组开不下呢...细细思考后发现可以开个tmp数组或滚动数组记录前一个枚举的塔的个数的最优解啊,然后总塔数减去两种塔的个数就是最后一种塔的个数呀!!然后就可以愉快的省下了一维啦!!我可真是个小机灵鬼!!
开心的带着自己美好的幻想打完了代码,然后就标准结局QwQ
看了题解之后感觉自己好zz呀qnq南瓜真的是太菜啦!
题解曰:初看这题会感觉很麻烦,因为三种塔并不是很好处理,但只要细心想就能发现: 激光塔放在前面的位置一定不如放在后面优。 也就是说激光塔的位置应该尽量靠后。 那么题目变简化成了在n个格子的前i个位置放置干扰塔和放射塔,后n-i个位置放置激光塔所造成的最大伤害。
然后我们就可以拿个二维dp[i][j]来表示前i个塔有j个干扰塔,则有i-j个放射塔,n-i个激光塔,然后按照题解简单膜你一下就好啦~
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#define maxn 2000
using namespace std;
__int128 n,r,g,b,t,ans;
__int128 dp[maxn][maxn];
int read()
{
int xx=0,kk=1;char ch=' ';
while(!isdigit(ch)){ch=getchar();if(ch=='-')kk=-1;}
while(isdigit(ch)){xx=xx*10+ch-'0';ch=getchar();}
return kk*xx;
}
void print(__int128 x)
{
if(x>9) print(x/10);
putchar(x%10+'0');
}
int main()
{
n=read(),r=read(),g=read(),b=read(),t=read();
for(int i=1;i<=n;++i)
dp[i][0]=(i-1)*g*t+dp[i-1][0];
for(int i=1;i<=n;++i)
for(int j=1;j<i;++j)
dp[i][j]=max(dp[i-1][j-1]+(i-j)*g*(t+(j-1)*b),dp[i-1][j]+(i-j-1)*g*(t+j*b));
for(int i=0;i<=n;++i)
for(int j=0;j<=i;++j)
ans=max(ans,dp[i][j]+r*(n-i)*(t+j*b)+(i-j)*g*(n-i)*(t+j*b));
print(ans);
return 0;
}