codeforces 597
D题
在一个二维平面上面,有n个城市,现在每个城市都没有电。
你可以选择一些城市建发电站,代价是c[i];你也可以给每个城市拉电线,给城市(i,j)之间拉电线的代价是(abs(x[i]-x[j])+abs(y[i]-y[j]))*(k[i]+k[j])。
现在问你最少花费多少代价,能够使得全部城市都有电,输出方案。
题解
建立一个超级源点,每个城市与超级源点连边,权重为第i个城市建设发电站的代价
问题就转化为普通最小生成树问题
prim kruskal都可以
注意保留下路径
与0节点相连的都是自建发电站
#include<bits/stdc++.h> using namespace std; #define int long long #define sc(x) scanf("%I64d",&x); #define si signed #define fi first #define se second #define pb push_back #define forn for(int i=0;i<n;i++) int n; #define P pair<int,int> P A[2005]; int C[2005]; int K[2005]; int d[2005]; bool vis[2005]; int mp[2005][2005]; int pre[2005]; vector<int>v; vector<P> vv; void prim() { // vis[0]=1; for(int i=1; i<=n; i++) { d[i]=1e18; } d[0]=0; for(int i=0; i<=n; i++) { int x=-1; for(int j=0; j<=n; j++) if(!vis[j]&&(x==-1||d[j]<d[x]))x=j; if(x==-1)break; vis[x]=1; for(int j=0; j<=n; j++) { if(!vis[j]){ if(d[j]>mp[x][j]){pre[j]=x;d[j]=mp[x][j];} } } } } si main() { sc(n) for(int i=1; i<=n; i++) { sc(A[i].fi) sc(A[i].se) } for(int i=1; i<=n; i++) { sc(C[i]) mp[i][0]=mp[0][i]=C[i]; } for(int i=1; i<=n; i++)sc(K[i]); for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) { mp[i][j]=(K[i]+K[j])*((labs(A[i].fi-A[j].fi))+(labs(A[i].se-A[j].se))); } } prim(); int ans=0; for(int i=0; i<=n; i++) { ans+=d[i]; } cout<<ans<<'\n'; memset(vis,0,sizeof vis);vis[0]=1; for(int i=1;i<=n;i++){ if(pre[i]==0){ v.push_back(i); vis[i]=1; } } cout<<v.size()<<endl; for(int i=0;i<v.size();i++)cout<<v[i]<<' '; cout<<endl; for(int i=1;i<=n;i++){ if(!vis[i]){ vv.push_back(P(i,pre[i])); vis[i]=1; } } cout<<vv.size()<<endl; for(int i=0;i<vv.size();i++)cout<<vv[i].fi<<' '<<vv[i].se<<endl; }
C
简单dp,字符串中他会把源字符串的w翻译成uu,把m变成nn,现在给你一个字符串问他的源字符串有多少种
#include<bits/stdc++.h> using namespace std; #define int long long #define sc(x) scanf("%I64d",&x); #define si signed #define fi first #define se second #define pb push_back #define forn for(int i=0;i<n;i++) char s[100005]; const int mod= 1e9+7; int dp[100005]; void init() { dp[0]=dp[1]=1; for(int i=2;i<=100000;i++){ dp[i]=(dp[i-1]+dp[i-2])%mod; } } si main() { init(); scanf("%s",s+1); int n=strlen(s+1); int ans=1; for(int i=1;i<=n;){ if(s[i]=='w'||s[i]=='m'){ puts("0"); return 0; } if(s[i]=='u'){ int k=0; while(i<=n){ if(s[i]=='u')k++,i++; else break; } ans=(ans*dp[k])%mod; }else if(s[i]=='n'){ int k=0; while(i<=n){ if(s[i]=='n')k++,i++; else break; } ans=(ans*dp[k])%mod; }else i++; } cout<<ans<<'\n'; }
E
期望dp
现在有一个10*10的矩阵,玩家要从左下角走到左上角,会先往右边走,再往左边走,再往右边走(看NOTE里面的图),然后这个图里面有一些梯子,用梯子可以爬到上面去。
现在这个人在掷骰子,问你期望最少花费多少次,能够到达终点。
如果你到终点前,你的骰子超过了终点,那么将不会移动。
首先dp【i】表示走到第i步的最小期望,由于在终点附件那里如果步数超过,那只能原地走
dp【1】=1//6*(dp0+1)+5/6(dp1+1)
dp【2】=1/6*(dp0+1)+1/6*(dp1+1)+2/3*(dp2+1)
由于dp0=0;
故dp1至dp5可求出来是6;
接下来的每个点就是,先不考虑地图的梯子,先考虑转移
就是dp【i】+=sigma(j=1~6)【(1/6)*d[i-j]】
当有梯子时,直接考虑要不要用梯子就行,取min最小值
#include<bits/stdc++.h> using namespace std; const int maxn = 15; double dp[maxn*maxn]; int idx[maxn][maxn]; int ladders[maxn*maxn]; int main(){ for(int i=0;i<10;i++){ for(int j=0;j<10;j++){ if(i%2){ idx[i][j]=(i*10+9-j); }else{ idx[i][j]=(i*10+j); } } } // for(int i=0;i<10;i++){ // for(int j=0;j<10;j++){ // cout<<idx[i][j]<<' '; // } // puts(""); // } for(int i=0;i<10;i++){ for(int j=0;j<10;j++){ int x;scanf("%d",&x); ladders[idx[i][j]]=idx[i-x][j]; } } for(int i=0;i<10;i++){ for(int j=0;j<10;j++){ cout<<ladders[idx[i][j]]<<' '; } puts(""); } dp[0]=0; for(int i=1;i<6;i++) dp[i]=6; for(int i=6;i<100;i++){ double sum = 0; for(int step=1;step<=6;step++){ sum = sum + min(dp[i-step],dp[ladders[i-step]]); } dp[i]=(sum/6)+1; } cout<< setprecision(10) << dp[99] << endl; }