T1:个人卫生综合征
题目描述:
每天BBS都要从家里经过城市中的一段路到学校刷五三。城市中一共有n个路口和m条双向道路,每条双向道路都连接着两个路口ai、bi且有一定的时间花费vi。BBS家编号为1,学校编号为n。今天,BBS由于个人卫生综合征导致他很迟才离开家,他想用膜法改变k条道路的长度使通过其的时间花费vi变为0。现在他问你改变道路长度之后他到学校的最小时间花费是多少?
输入格式:
第一行为三个整数n、m、k,接下来的m行每行三个整数ai,bi,vi,分别表示这条路连着的两个路口和通过其所用的时间。
输出格式:
一个整数,表示BBS到学校的最小时间花费。
样例输入 |
样例输出 |
4 4 1 |
1 |
样例解释:
更新3->4的道路,最短路线为1->3->4,用时为1+0=1。
数据范围:
对于100%的数据:1<=n<=10000,1<=m<=50000,1<=k<=20,1<=vi<=1000000。
题解:建分层图,然后跑最短路即可。看代码即可理解。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<vector> 8 #include<queue> 9 #define inf 0x3f3f3f3f 10 using namespace std; 11 struct path{ 12 int to,val; 13 }; 14 struct node{ 15 int num,v; 16 bool operator <(const node &b) const{ 17 return v>b.v; 18 } 19 }; 20 std::vector<path> g[10010]; 21 std::priority_queue<node> q; 22 int m,n,k,ans; 23 int dist[10010][25]; 24 bool vis[10010][25]; 25 void dij(){ 26 node now; 27 memset(dist,inf,sizeof(dist)); 28 memset(vis,0,sizeof(vis)); 29 dist[1][0]=0; 30 int i,j; 31 q.push((node){1,0}); 32 while(!q.empty()){ 33 now=q.top();q.pop(); 34 int ta=now.num%(n+1),tb=now.num/(n+1); 35 vis[ta][tb]=true; 36 for(i=0;i<int(g[ta].size());++i){ 37 path t=g[ta][i]; 38 if(!vis[t.to][tb] && dist[t.to][tb]>dist[ta][tb]+t.val){ 39 dist[t.to][tb]=dist[ta][tb]+t.val; 40 q.push((node){tb*(n+1)+t.to,dist[t.to][tb]}); 41 } 42 if(!vis[t.to][tb+1] && tb<k && dist[t.to][tb+1]>dist[ta][tb]){ 43 dist[t.to][tb+1]=dist[ta][tb]; 44 q.push((node){(tb+1)*(n+1)+t.to,dist[t.to][tb+1]}); 45 } 46 } 47 } 48 } 49 int main(){ 50 freopen("school.in","r",stdin); 51 freopen("school.out","w",stdout); 52 scanf("%d%d%d",&n,&m,&k); 53 int i,j; 54 int a,b,v; 55 for(i=1;i<=m;++i){ 56 scanf("%d%d%d",&a,&b,&v); 57 g[a].push_back((path){b,v}); 58 g[b].push_back((path){a,v}); 59 } 60 dij(); 61 ans=inf; 62 for(i=0;i<=k;++i) ans=min(ans,dist[n][i]); 63 printf("%d\n",ans); 64 return 0; 65 }
T2:你的四边形已如风中残烛
题目描述:
LGL有一根长为n的木板。现在他想要把它砍成四段长度为整数的木板来做一个四边形,请问他有多少种不同的砍法?注意:四段长度为1、1、2、1和四段长度为1、2、1、1算两种砍法。
输入格式:
第一行为一个整数 n,表示木板的长度。
输出格式:
一个整数,不同的砍法数量。
样例输入 |
样例输出 |
6 |
6 |
样例解释:
1122,1212,1221,2112,2121,2211。
数据范围:
对于100%的数据:1<=n<=2500。
题解:记dp[i][j]为长度为i时分成j段的方案数,注意一段长度小于总长的一般。dp[i][j]可以从dp[i-k][j-1]转移而来(k表示单段长度)。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 #include<cmath> 7 using namespace std; 8 int n,mid; 9 int dp[2510][5]; 10 int min(int a,int b){ 11 return a<b?a:b; 12 } 13 int main(){ 14 freopen("quad.in","r",stdin); 15 freopen("quad.out","w",stdout); 16 scanf("%d",&n); 17 memset(dp,0,sizeof(dp)); 18 mid=(n-1)>>1; 19 dp[0][0]=1; 20 int i,j,k; 21 for(i=1;i<=n;++i){ 22 for(j=1;j<=4;++j){ 23 for(k=1;k<=min(i,mid);++k){ 24 dp[i][j]+=dp[i-k][j-1]; 25 } 26 } 27 } 28 printf("%d\n",dp[n][4]); 29 return 0; 30 }
T3:生命不息刷题不止
题目描述:
YYH有n道题要做。但是由于他上课做某些事,导致他一题都不会做,只好请LGL代打。LGL为了买自己心爱的坦克,他做第i题要收两笔钱:一笔在YYH叫他做题当天收,另外一笔在叫他做题的第二天收。YYH每天结束的时候都会把剩下的所有钱花光,然后再从父亲LRB处得到m元零花钱用来请LGL做题(也就是说,第一天的时候YYH是没有钱请LGL做题的,每一天用来请LGL做题所用的钱都是前一天LRB给的)。而且,YYH做的题目难度是循序渐进的:就算强如LGL,在做第i题之前也要先把第1到i-1题全部做完。请问YYH将所有题目做完并且把所有钱都付给LGL的最小天数。
输入格式:
第一行为两个整数m、n,接下来的n行每一行都有两个数ai和bi,分别表示LGL做第i题所收的两笔钱。
输出格式:
一个整数,表示最小天数。
样例输入 |
样例输出 |
100 5 |
6 |
样例解释:
第二天做1、2两题,第三天做3、4两题,第五天做5。在第六天的时候所有钱都付完。
数据范围:
对于100%的数据:1<=n<=300,1<=ai、bi<=m<=1000。
题解:记dp[i][j]为前i天做了j道题第i天最多能够剩下的钱的数量。因此,dp[i][j]可以转移到dp[i][j+1],dp[i][j+2]……只要满足剩下的钱大于0且下一天的钱够才行。同时,也可以算出dp[i+1][j+1],dp[i+1][j+2]……最后输出dp[i][m]的最小存在值i即可。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #define inf 0x3f3f3f3f 8 using namespace std; 9 int n,m; 10 int ta,tb; 11 int max(int a,int b){ 12 return a>b?a:b; 13 } 14 int dp[1010][310]; 15 int a[310],b[310]; 16 int main(){ 17 freopen("solve.in","r",stdin); 18 freopen("solve.out","w",stdout); 19 scanf("%d%d",&m,&n); 20 int j; 21 for(int i=1;i<=n;++i){ 22 scanf("%d%d",&a[i],&b[i]); 23 } 24 memset(dp,inf,sizeof(dp)); 25 dp[2][0]=m; 26 int i=2; 27 while(dp[i][n]==inf){ 28 for(j=n;j>=0;j--){ 29 if(dp[i][j]!=inf){ 30 int k=j+1; 31 ta=dp[i][j];tb=dp[i+1][j]=m; 32 while(ta>=a[k] && tb>=b[k]){ 33 ta-=a[k];tb-=b[k]; 34 dp[i][k]=(dp[i][k]==inf?ta:max(dp[i][k],ta)); 35 dp[i+1][k]=(dp[i+1][k]==inf?tb:max(dp[i+1][k],tb)); 36 k++; 37 } 38 } 39 } 40 i++; 41 } 42 printf("%d\n",i); 43 return 0; 44 }
T4:短
题目描述:
给出一张有n个点和m条双向边的图,要求求出1到n的次短路的长度。一条边可以多次通过。
输入格式:
第一行为两个整数n和m。接下来的m行每行三个整数ai,bi,vi,分别表示这条路连着的两个点和他的长度。
输出格式:
一个整数,表示次短路的长度。
样例输入 |
样例输出 |
4 4 |
450 |
样例解释:
最短:1->2->4。
次短:1->2->3->4。
数据范围:
对于 100%的数据:1<=n、vi<=5000,1<=m<=100000。
题解:考虑dist[i]为1到i的最短路,dist2[i]表示从i到n的最短路次短路有两种情况:一是从1走dist[i]到i,从i到j,走dist2[j]到n;二是从1走dist[i]到i,从i到j再回到i,走dist2[i]到n,暴力枚举即可(似乎dij也有一种算法,但我打挂了)。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<iostream> 6 #include<cmath> 7 #include<queue> 8 #define inf 0x3f3f3f3f 9 struct path 10 { 11 int from,to,v,next; 12 }g[200001]; 13 int min(int a,int b){ 14 return a<b?a:b; 15 } 16 std::queue<int> q; 17 int n,m,tot,mn,ans; 18 int last[5001],dist[5001],dist2[5001]; 19 bool inq[5001]; 20 void ins(int a,int b,int v) 21 { 22 g[++tot].from=a;g[tot].to=b;g[tot].v=v;g[tot].next=last[a];last[a]=tot; 23 g[++tot].from=b;g[tot].to=a;g[tot].v=v;g[tot].next=last[b];last[b]=tot; 24 } 25 void spfa() 26 { 27 memset(inq,false,sizeof(inq)); 28 memset(dist,inf,sizeof(dist)); 29 dist[1]=0; 30 q.push(1); 31 inq[1]=true; 32 while(!q.empty()) 33 { 34 int now=q.front(); 35 q.pop(); 36 inq[now]=false; 37 for(int i=last[now];i>0;i=g[i].next) 38 { 39 int t=g[i].to; 40 if(dist[t]>dist[now]+g[i].v) 41 { 42 dist[t]=dist[now]+g[i].v; 43 if(!inq[t]) 44 { 45 q.push(t); 46 inq[t]=true; 47 } 48 } 49 } 50 } 51 } 52 void spfa2() 53 { 54 memset(inq,false,sizeof(inq)); 55 memset(dist2,inf,sizeof(dist)); 56 dist2[n]=0; 57 q.push(n); 58 inq[n]=true; 59 while(!q.empty()) 60 { 61 int now=q.front(); 62 q.pop(); 63 inq[now]=false; 64 for(int i=last[now];i>0;i=g[i].next) 65 { 66 int t=g[i].to; 67 if(dist2[t]>dist2[now]+g[i].v) 68 { 69 dist2[t]=dist2[now]+g[i].v; 70 if(!inq[t]) 71 { 72 q.push(t); 73 inq[t]=true; 74 } 75 } 76 } 77 } 78 } 79 int main() 80 { 81 freopen("short.in","r",stdin); 82 freopen("short.out","w",stdout); 83 scanf("%d%d",&n,&m); 84 for(int i=1;i<=m;i++) 85 { 86 int x,y,l; 87 scanf("%d%d%d",&x,&y,&l); 88 ins(x,y,l); 89 } 90 spfa(); 91 mn=dist[n]; 92 spfa2(); 93 ans=inf; 94 for(int i=1;i<=tot;i++) 95 { 96 int l=dist[g[i].from]+dist2[g[i].to]+g[i].v; 97 if(l<ans&&l>mn) ans=l; 98 } 99 for(int i=1;i<=tot;i++) 100 ans=min(ans,dist[g[i].from]+dist2[g[i].from]+2*g[i].v); 101 printf("%d",ans); 102 return 0; 103 }